actor模型如何滿足現代分散式系統的需求
阿新 • • 發佈:2020-10-10
akka版本2.6.9
版權宣告:本文為博主原創文章,未經博主允許不得轉載。
正如在前面的主題中所描述的,常見的程式設計實踐不能正確地滿足現代系統的需求。值得慶幸的是,我們不需要放棄我們所知道的一切。相反,actor模型以一種有原則的方式解決了這些缺點,允許系統以一種更好地匹配我們的心智模型的方式執行。角色模型抽象允許您從通訊的角度來考慮程式碼,就像在大型組織中人與人之間進行的交換一樣。
使用actor可以讓我們解決如下問題:
- 在不使用鎖的情況下實施封裝。
- 使用協作實體對訊號做出反應、改變態並相互發送訊號的模型來推動整個應用程式向前發展。
- 不要擔心執行機制與我們的全域性不匹配。
訊息傳遞的使用避免了鎖定和阻塞
actor互相傳送訊息,而不是呼叫方法。傳送訊息並不會將執行執行緒從傳送方轉移到目的地。actor可以傳送訊息並在不阻塞的情況下繼續。因此,它可以在相同的時間內完成更多的任務。
對於物件,當方法返回時,它釋放對其執行執行緒的控制。在這方面,actor的行為很像物件,它們對訊息做出反應,並在完成當前訊息的處理後返回執行。這樣,actor實際上實現了我們對物件的計劃執行:
傳遞訊息和呼叫方法之間的一個重要區別是訊息沒有返回值。通過傳送訊息,參與者將工作委託給另一個參與者。正如我們在呼叫堆疊的假象中看到的那樣,如果它期望返回值,傳送actor要麼需要阻塞,要麼需要在同一執行緒上執行另一個actor的工作。相反,接收actor以回覆訊息的形式交付結果。
由於每個actor最多隻處理一條訊息,因此可以在不同步的情況下保持actor的不變數。這是自動發生而不使用鎖:
總之,當actor收到訊息時,會發生以下情況:
- actor將訊息新增到佇列的末尾。
- 如果actor沒有計劃執行,它將被標記為準備執行。
- 一個(隱藏的)排程程式實體接受actor並開始執行它。
- actor從佇列前端獲取訊息。
- actor修改內部狀態,向其他actor傳送訊息。
- 這個actor沒有安排處理。
為了完成這個行為,actor有:
- 郵箱(訊息在其中結束的佇列)。
- 行為(actor的狀態,內部變數等)。
- 訊息(表示訊號的資料片段,類似於方法呼叫及其引數)。
- 執行環境(接受具有響應訊息的actor並呼叫其訊息處理程式碼的機制)。
- 一個地址(稍後詳細介紹)。
訊息進入actor郵箱。actor的行為描述了actor如何響應訊息(比如傳送更多訊息和/或更改狀態)。執行環境編排一個執行緒池,以完全透明地驅動所有這些操作。
這是一個非常簡單的模型,它解決了前面列舉的問題:
- 封裝是通過將執行與信令解耦來保持的(方法呼叫傳輸執行,訊息傳遞不傳輸)。
- 不需要鎖。只能通過訊息修改actor的內部狀態,在嘗試保持不變時,一次處理一個訊息以消除競爭。
- 任何地方都沒有使用鎖,傳送方也沒有被阻塞。數百萬個actor可以被有效地安排在十幾個執行緒上,從而達到現代cpu的全部潛力。任務委託是actor的自然操作模式。
- actor的狀態是本地的而不是共享的,更改和資料通過訊息傳播,這對映到現代記憶體層次結構的實際工作方式。在許多情況下,這意味著只通過包含訊息中的資料的快取記憶體線進行傳輸,同時保持本地狀態和快取在原始核心中的資料。相同的模型完全對映到遠端通訊,其中狀態儲存在機器的RAM中,更改/資料以包的形式在網路上傳播。
actor優雅地處理錯誤情況
由於在相互發送訊息的actor之間不再有共享的呼叫堆疊,因此我們需要以不同的方式處理錯誤情況。我們需要考慮兩種誤差:
- 第一種情況是由於任務中的錯誤(通常是一些驗證問題,如不存在的使用者ID)導致目標actor上的委託任務失敗。在這種情況下,由目標actor封裝的服務是完整的,只有任務本身是錯誤的。服務actor應該用一條訊息回覆傳送者,顯示錯誤情況。這裡沒有什麼特別的,錯誤是域的一部分,因此成為普通的訊息。
- 第二種情況是服務本身遇到內部錯誤。Akka強制所有actor被組織成一個類似樹的層次結構,即建立另一個actor的actor成為新參與者的父參與者。這與作業系統將程序組織成樹的方式非常相似。就像程序一樣,當一個actor失敗時,它的父actor可以決定如何對失敗做出反應。另外,如果父actor被停止,那麼它的所有子actor也將被遞迴停止。這項服務被稱為監督,它是Akka的核心。
管理器策略通常由父actor在啟動子actor時定義。它可以決定在某些型別的失敗時重啟child actor,或者在其他型別的失敗時完全停止它。孩子永遠不會無聲無息地死去(進入無限迴圈的明顯例外),相反,他們要麼失敗,主管策略可以對錯誤作出反應,要麼停止(在這種情況下,相關方會得到通知)。總是有一個負責管理actor的實體:它的父實體。重新啟動從外部是不可見的:當目標actor重新啟動時,合作actor可以繼續傳送訊息。