1. 程式人生 > >【AKKA 官方文件翻譯】為什麼現代系統需要一個新的程式設計模型

【AKKA 官方文件翻譯】為什麼現代系統需要一個新的程式設計模型

為什麼現代系統需要一個新的程式設計模型

akka版本2.5.8
版權宣告:本文為博主原創文章,未經博主允許不得轉載。

actor模型是由Carl Hewitt在數十年前提出的,這個模型提供了一種在高效能網路中進行並行處理的方式,然而這種環境在當時還尚不存在。現如今,硬體和基礎設定的效能已經達到並超越了Hewitt的願景。一些組織在構建具有苛刻要求的分散式系統時經常會遇到挑戰,這些問題已經無法使用傳統OOP程式設計模型來完全解決。這種情況下,他們可以在actor模型中獲得幫助。

現在actor模型已經成為了一種公認的高效解決方案,並且在一些要求最苛刻的系統中得到了證明。為了體現actor模型的價值,本節主要討論在現代多執行緒、多CPU結構之上使用傳統程式設計手段的一些問題。

封裝的挑戰

OOP程式設計的核心是封裝。封裝要求物件內的資料不能被外部直接訪問,它們只能通過方法呼叫被改變。物件有義務對外提供安全的操作方法以維持其內部封裝資料的特性。

例如,一個有序二叉樹的操作不能改變其內部元素順序不變的性質,在做樹的查詢時,呼叫者會依賴這些性質進行程式的操作。
當我們分析OOP執行時的行為時,我們可以繪製以下的資訊序列圖來表現方法的呼叫:

這裡寫圖片描述

不幸的是,上面的圖並不能準確地表示程式生命週期的執行過程。實際上,這些程式是在單執行緒上執行的,並且這些OOP內部不變特性的執行發生在呼叫該方法的相同執行緒上,圖表中加入執行執行緒如下:

這裡寫圖片描述

當我們嘗試使用多執行緒進行建模時,我們會發現我們的圖表變得不那麼合適了。以下是多執行緒同時訪問同一個示例的圖表:

這裡寫圖片描述

有兩個執行緒同時呼叫了同一個方法。但是在這種情況下,物件的封裝模型並不能保證該呼叫得到預期的結果。兩個執行緒執行的指令可能以任意的方式進行交錯,因此在沒有正確協調兩個執行緒的情況下,物件的內部不變特性在執行之後是不可預知的,更不用說在多個執行緒同時呼叫下的結果了。

通常情況下,為了解決這個問題,我們會為這個方法新增一個鎖。鎖可以保證在任何時刻只會有一個執行緒進入這個方法。但是這是一種很消耗資源的策略:

1、鎖嚴重限制了執行緒的併發。加鎖在現代CPU架構上是一個很消耗資源的方式,需要作業系統掛起執行緒在之後進行恢復,這對作業系統來說是一個重擔。

2、呼叫執行緒會被阻塞,因此這個執行緒在這段時間內不能去做其他工作。即使在桌面應用程式中,這也是不可接受的。我們希望在後臺即使有耗時很長的程式執行情況下,使用者介面也可以響應。對後端而言,執行緒阻塞是一種極大的浪費,有人認為這個情況可以通過啟動一個新的執行緒來進行補償,但是執行緒也是一個昂貴的抽象。

3、鎖會引入一個新的威脅:死鎖

這些問題導致出現了一些糾結的情形:

1、如果沒有足夠的鎖,狀態會被多執行緒破壞

2、如果加入了大量的鎖,程式的效能會受到很大的影響,並且很容易產生死鎖。

另外,鎖只能在本地很好地執行。當我們涉及到多臺機器協作時,唯一的辦法只能是使用分散式鎖。不幸的是,分散式鎖的效率要比本地鎖低幾個數量級,並且通常情況下對機器的擴充套件性限制很大。分散式鎖協議需要在多臺機器之間通過網路進行多次往返通訊,因此延遲會飛速增長。

在面嚮物件語言中,我們很少會去考慮執行緒的執行路徑。我們經常將系統設想為一個物件例項的網路。它對方法呼叫作出反應,並修改它們的內部狀態。然後通過方法呼叫相互通訊,從而驅動整個應用程式執行:
這裡寫圖片描述

然而,在多執行緒分散式環境下,執行緒像下圖一樣通過方法呼叫來“遍歷”物件例項網路。結果執行緒成為真正的程式驅動:

這裡寫圖片描述

總結:

1、物件只能在單執行緒訪問時保證封裝(保護其內部不變特性),多執行緒執行幾乎總是導致內部狀態的破壞。物件內部的不變特性可能被兩個執行同一程式碼的競爭執行緒改變。

2、在當前看來,鎖似乎是維護多執行緒先物件封裝的自然選擇。但實際上它們效率低下,並且它們很容易導致死鎖。

3、鎖工作在本地,試圖把他們擴充套件到分散式系統中,但是限制了擴充套件能力。

在現代計算機體系結構上共享記憶體的錯覺

在80-90年代的程式設計模型的概念中,寫入一個變數意味著直接寫入記憶體中的一個地址(區域性變數可能只存在於暫存器中)。在現代架構上,我們稍微簡化一下,CPU寫入快取(cache line)中而不是直接寫入記憶體。大多數這些快取記憶體是存在於CPU核心本地的,也就是說一個核寫入的資料是對其他核不可見的。為了使核心的本地修改對其他核可見,從而可以對其他執行緒可見,需要將快取記憶體傳送到另一個核心的快取記憶體中。

在JVM上,我們必須通過使用volatile標記或者Atomic包來明確指出這些記憶體地址是要被執行緒間共享的。否則,我們只能使用鎖在臨界區訪問它們。這麼看來為什麼我們不把所有的變數都宣告為volatile變數呢?因為在不同的核之間傳遞快取是非常昂貴的操作,這會潛在地拖慢所涉及到的核心的執行,並導致快取記憶體一致性協議的瓶頸(CPU使用這個協議在主儲存器和其他CPU間傳遞快取記憶體行),最終導致的執行效率劇烈下降。

即使開發人員意識到這種情況,在何處使用volatile或者何處使用atomic結構依舊是個難題。

總結:

1、再也沒有真正的共享記憶體了。CPU核心就像網路上的計算機一樣,將資料塊(快取行)顯式地傳遞給對方。CPU間的通訊和網路通訊有很多共同之處。它們通過標準來傳遞訊息。

2、更好的方法是線上程本地保持狀態,並且使用可併發的物件傳送資訊的方式向其他執行緒顯式地傳遞資料或事件。用這種方式替代一些volatile標記和atomic結構是更好的選擇。

呼叫堆疊的錯覺

今天,我們經常使用堆疊結構,但是它們是在一個併發程式設計並不重要的時代發明的,因為在那時候多CPU系統並不常見。堆疊不會被跨執行緒呼叫,因此不需要模擬非同步呼叫鏈。

當執行緒打算將任務委託給一個“後臺”時出現了問題。實際上,這意味著任務委託給了另一個執行緒。這種委託和方法呼叫是不同的,因為方法呼叫是嚴格在同一個執行緒本地進行的。我們經常看到呼叫者將一個物件放入一個和工作執行緒共享的記憶體位置,然後工作執行緒在某個事件迴圈中進行拾取。這允許呼叫者執行緒不被阻塞繼續進行內其他的任務。

但是我們要如何通知呼叫者該任務已經完成了?並且,如果一個任務失敗並且產生了一個異常,則會出現更嚴重的問題。異常會傳播到什麼地方?它將傳播到工作執行緒的異常處理程式裡,完全忽略呼叫者是誰:

這裡寫圖片描述

這是一個嚴重的問題,工作先稱該如何處理這種情況?因為它通常不知道這個失敗的任務是為了做什麼。這時需要通過某種方式通知呼叫者,但是沒有呼叫堆疊來解除異常。失敗通知只能被另外的通道處理,例如把錯誤碼放在呼叫者取結果的地方。如果這個結果沒有放到位,呼叫者就永遠得不到任務失敗通知,從而導致任務丟失。這種情況與聯網系統的工作方式非常相似,即訊息/請求可能在沒有任何通知的情況下丟失/失敗。

當真正的錯誤發生或者執行線上程上的工作者由於bug導致不可恢復的失敗時,情況會變得更糟。舉例來說,當一個bug引起一個內部的異常,並且異常冒泡到執行緒根部導致執行緒關閉。問題來了,應該由誰來重啟這個執行緒所執行的服務?怎樣恢復它到一個正確的狀態?這看起來似乎是可以解決的,但是還有另一個問題,執行緒失敗的時候正在執行的這個任務、其執行狀態已經完全丟失了,我們丟失了一個資訊,即使這是一個本地通通訊,沒有涉及到網路。

總結:

1、為了在當今系統上實現好的併發,執行緒需要使用高效的方式來給其他執行緒委託工作,並且不被阻塞。在這種使用任務委託方式進行併發(在網路化/分散式計算中更是如此)呼叫情況下,基於堆疊的錯誤處理將會崩潰,一個新的明確的錯誤訊號機制需要被引入。失敗需要成為領域模型的一部分。

2、工作委託下的併發系統需要處理服務故障,並且有方法去恢復它們。這些服務的客戶端需要知道在重新啟動期間,任務/訊息可能會丟失。即使沒有丟失,響應也可能會被之前很長的任務佇列、GC等延遲。面對這些情況,併發系統需要像網路/分散式系統一樣加入超時機制來處理響應的期限。

接下來,讓我們一起看看actor模型是如何應對這些挑戰的。

相關推薦

AKKA 官方翻譯為什麼現代系統需要一個程式設計模型

為什麼現代系統需要一個新的程式設計模型 akka版本2.5.8 版權宣告:本文為博主原創文章,未經博主允許不得轉載。 actor模型是由Carl Hewitt在數十年前提出的,這個模型提供了一種在高效能網路中進行並行處理的方式,然而這種環境在當

Gradle官方翻譯起步2:建立構建掃描

構建掃描是對構建的可分享的專門記錄,可以看到“構建中發生了那些行為以及為什麼會發生這種行為”。通過在專案中使用構建掃描外掛,開發者可以免費地在https://scans.gradle.com/上釋出構建掃描。 將要建立的 本文會展示如何在不對任何構建指令碼進行

Unity3D技術翻譯第1.6篇 使用 AssetBundle Manager

上一章:【Unity3D技術文件翻譯】第1.5篇 使用 AssetBundles 本章原文所在章節:【Unity Manual】→【Working in Unity】→【Advanced Development】→【AssetBundles】→【AssetBundle Manager】 As

ApiDoc官方(翻譯)

一、apidoc簡介 apidoc是一款可以有原始碼中的註釋直接自動生成api介面文件的工具,它幾乎支援目前主流的所有風格的註釋。例如: Javadoc風格註釋(可以在C#, Go, Dart, Java, JavaScript, PHP, TypeScript等語言中

Android官方翻譯Android官方-Activities(一)

Activity是可以給使用者提供互動操作的程式元件,例如打電話,拍照,傳送郵件,抑或者是顯示地圖。通常視窗會填滿螢幕,但是也可以做到比螢幕小或者是懸浮在視窗頂部。 App通常由多個Activities組成,它們之間支援相互跳轉。一般情況下,每個Activit

AutoMapper官方DTO與Domin Model相互轉換(上)

寫在前面   AutoMapper目錄:   本篇目錄:   上一篇《【道德經】漫談實體、物件、DTO及AutoMapper的使用 》,因為內容寫的有點跑偏,關於AutoMapper的使用最後只是簡單寫了下,很明顯這種簡單的使用方式不能滿足專案中複雜的需要,網上找了下AutoMapper相關文件

AutoMapper官方DTO與Domin Model相互轉換(中)

寫在前面   AutoMapper目錄:   本篇目錄:   隨著AutoMapper的學習深入,發現AutoMapper在物件轉換方面(Object-Object Mapping)還蠻強大的,當時使用AutoMapper的場景是DTO與Domin Model相互轉換,所以文章的標題就是這個(標

AutoMapper官方DTO與Domin Model相互轉換(下)

寫在前面   AutoMapper目錄:   本篇目錄:   關於AutoMapper寫到這基本的東西都差不多了,上一篇定義為靈活配置篇,本篇可以定義為擴充套件應用篇,加一些補充,關於AutoMapper的專案應用,網上找了幾篇英文文章,雖然看不懂,但是程式碼是相通的,感覺很不錯,主要是Enti

android官方android AIDL

概述      AIDL(安卓介面解釋語言)和其他的IDLs類似。可以定義程式介面讓客戶端和service進行跨程序的通訊(IPC)。在android中,一個程序通常不能訪問另一個程序的記憶體。所以,他們的物件需要被分解成更原始的單位,直到系統可以理解,並且集結這些物件穿

pytest官方解讀fixtures - 1.什麼是fixtures

在深入瞭解fixture之前,讓我們先看看什麼是`測試`。 ### 一、測試的構成 其實說白了,測試就是在特定的環境、特定的場景下、執行特定的行為,然後確認結果與期望的是否一致。 就拿最常見的登入來說,完成一次正常的登入場景,需要可用的測試環境,可以正常登入的賬號和密碼。 然後,用這個賬號密碼進行登入操

pytest官方解讀fixtures - 2. fixtures的呼叫方式

既然fixtures是給執行測試做準備工作的,那麼pytest如何知道哪些測試函式 或者 fixtures要用到哪一個fixtures呢? 說白了,就是fixtures的呼叫。 ### 一、測試函式宣告傳參請求fixture 測試函式通過將fixture宣告為引數來請求fixture。 ``` def te

pytest官方解讀fixtures - 8. yield和addfinalizer的區別(填坑)

在[上一章](https://www.cnblogs.com/pingguo-softwaretesting/p/14479170.html)中,文末留下了一個坑待填補,疑問是這樣的: 目前從官方文件中看到的是 ``` We have to be careful though, because pytest

Unity3D技術翻譯第1.1篇 AssetBundle 工作流

如何 倉庫 ring 資源 string int 觀察 你是 本地 譯者前言:本章是關於從創建到加載,再到使用 AssetBundle 的整個流程的概述。閱讀本章將對 AssetBundle 的工作流程有個簡單而全面的了解。 本章原文所在章節:【Unity Manual】

#pragma comment 官方翻譯

一 格式 #pragma comment(comment-type [,commentstring] ) 作用 Places a comment record into an object file or executable file. 翻譯:將一個註釋記錄放入一個object檔案

著色器shader官方翻譯

近期打算好好學習shader,期望能做出大海波浪等絢麗的效果。 官方文件的翻譯,算是我的附帶產出,增強對shader的瞭解,也是為後人參考學習提供捷徑。 ---------------------------------------------------------------------

Hibernate官方翻譯-(第二章,入門)

第二章:使用原生Hibernate API 和hbm.xml對映 (示例程式碼下載地址http://sourceforge.net/projects/hibernate/files/hibernate4/) 2.1. Hibernate configuration 檔案 資原始檔hibern

Android官方翻譯PagerAdapter

PagerAdapter public abstract class PagerAdapter extends Object (公共抽象類 繼承自object) java.lang.Object ↳ androidx.viewpager.widget.PagerAdapter

swupdate嵌入式系統的軟體管理

嵌入式系統的軟體管理 嵌入式系統變得越來越複雜, 它們的軟體也反映了這種複雜性的增加。 為了支援新的特性和修復,很有必要讓嵌入式系統上的軟體 能夠以絕對可靠的方式更新。 在基於linux的系統上,我們可以在大多數情況下找到以下元素: 引導裝載程式 核心和裝置樹 根檔案系統 其他在後續掛載

AKKA官方閱讀筆記(1)JAVA版2.5.16

準備工作: Actor層級結構 其實在你用程式碼建立Actor之前,Akka自己就已經建立三個actor了,它們都是負責監管自己下面的actor的: / 這個就是傳說中的跟監管者,是所有actor的祖先,當系統終止時,它一定是最後一個被停止的 /user

Pulsar官方翻譯-入門必看-概念和架構-(一)概覽(Pulsar Overview)

官網原文標題《Concepts and Architecture--Pulsar Overview》 翻譯時間:2018-09-28 譯者:本文介紹了Pulsar的起源和現狀,以及主要特性。 後續閱讀:《Messaging Concepts》 譯者序言: 由