分佈式計算模式之Actor

前言

前幾篇文章介紹了MapReduce 和 Stream 計算模式,雖然這兩種計算模式對數據的處理方式不同,但都是以特定數據類型(分別對應靜態數據和動態數據)作爲計算維度。在接下來兩篇文章中,將從計算過程處理過程的維度,介紹另外兩種分佈式計算模式,即 Actor 和流水線。

分佈式計算的本質就是在分佈式環境下,多個進程協同完成一件複雜的事情,但每個進程各司其職,完成自己的工作後,再交給其他進程去完成其他工作。而分佈式進程那麼多,如果需要開發者自己去維護每個進程之間的數據、狀態等信息,這個開發量可不是一般得大,而且特別容易出錯。那麼,有沒有什麼辦法可以讓開發者只關注自己的邏輯呢?答案是肯定的,Actor 計算模式就能滿足你的需求。

什麼是 Actor?

在上文《分佈式體系結構之非集中式結構》中提到 Akka 框架基於 Actor 模型,提供了一個用於構建可擴展的、彈性的、快速響應的應用程序的平臺。
其中,Actor 類似於一個“黑盒”對象,封裝了自己的狀態和行爲,使得其他 Actor 無法直接觀察到它的狀態,調用它的行爲。多個 Actor 之間通過消息進行通信,這種消息類似於電子郵箱中的郵件。Actor 接收到消息之後,纔會根據消息去執行計算操作。
Actor 模型,代表一種分佈式並行計算模型。這種模型有自己的一套規則,規定了 Actor 的內部計算邏輯,以及多個 Actor 之間的通信規則。在 Actor 模型裏,每個 Actor 相當於系統中的一個組件,都是基本的計算單元。Actor 模型的計算方式與傳統面向對象編程模型(Object-Oriented Programming,OOP)類似,一個對象接收到一個方法的調用請求(類似於一個消息),從而去執行該方法。
但是,OOP 因爲數據封裝在一個對象中,不能被外部訪問,當多個外部對象通過方法調用方式,即同步方式進行訪問時,會存在死鎖、競爭等問題,無法滿足分佈式系統的高併發性需求。而 Actor 模型通過消息通信,採用的是異步方式,克服了 OOP 的侷限性,適用於高併發的分佈式系統。
舉一個最簡單的例子,假如你現在定義了三個對象 A、B 和 C,對象 C 中有一個函數 Function,現在對象 A 和對象 B 同時調用對象 C 中的 Function,此時對象 C 中的 Function 就成爲了共享資源,有可能會存在競爭、死鎖等問題。
而對於 Actor 模式,對象 A、B 和 C 對應着 Actor A、Actor B 和 Actor C,當 Actor A 和 Actor B 需要執行 Actor C 中的 Function 邏輯時,Actor A 和 Actor B 會將消息發送給 Actor C, Actor C 的消息隊列存儲着 Actor A 和 Actor B 的消息,然後根據消息的先後順序,執行 Function 即可。也就是說,Actor 模式採用了異步模式,並且每個 Actor 封裝了自己的數據、方法等,解決了 OOP 存在的死鎖、競爭等問題。

Actor 計算模式

如下圖所示,描述了具有 3 個 Actor 的 Actor 模型。
在這裏插入圖片描述
Actor 模型的三要素是狀態行爲消息。Actor 模型 =(狀態 + 行爲)+ 消息。

  • 狀態(State)。Actor 的狀態指的是,Actor 組件本身的信息,相當於 OOP 對象中的屬性。Actor 的狀態會受 Actor自身行爲的影響,且只能被自己修改
  • 行爲(Behavior)。Actor 的行爲指的是,Actor 的計算處理操作,相當於 OOP對象中的成員函數。Actor 之間不能直接調用其他 Actor 的計算邏輯。Actor只有收到消息纔會觸發自身的計算行爲。
  • 消息(Mail)。Actor 的消息以郵件形式在多個 Actor 之間通信傳遞,每個 Actor會有一個自己的郵箱(MailBox),用於接收來自其他 Actor 的消息,因此 Actor模型中的消息也稱爲郵件。一般情況下,對於郵箱裏面的消息,Actor 是按照消息達到的先後順序(FIFO)進行讀取和處理的。

Actor 工作原理

爲了方便理解 Actor 的工作原理,現通過講述 3 個 Actor 之間基於消息和消息隊列的工作流程進行說明。
這 3 個 Actor 的工作流程,如下所示:
在這裏插入圖片描述

  1. Actor1 和 Actor3 先後向 Actor2 發送消息,消息被依次放入 Actor2 的 MailBox 隊列的隊尾;
  2. Actor2 從 MailBox 隊列的隊首依次取出消息執行相應的操作,由於 Actor1 先把消息發送給 Actor2,因此Actor2 先處理 Actor1 的消息;
  3. Actor2 處理完 Actor1 的消息後,更新內部狀態,並且向其他 Actor 發送消息,然後處理 Actor3 發送的消息。

瞭解了 Actor 之間的消息交互和處理流程,我再以一個具體案例和你詳細解讀一下 Actor 之間的消息傳遞過程吧。
在這裏插入圖片描述
在系統中,不同的組件 / 模塊可以視爲不同的 Actor。現在有一個執行神經網絡的應用,其中有兩個組件 A 和 B,分別表示數據處理模塊和模型訓練模塊。假設,我們可以將組件 A 和 B 看作兩個 Actor,訓練過程中的數據可以通過消息進行傳遞。如上圖所示,完整的消息傳輸過程爲:
1 . 組件 A 創建一個 Actor System,用來創建並管理多個 Actor。
2 . 組件 A 產生 QuoteRequest 消息(即 mail 消息,比如數據處理後的數據),並將其發送給 ActorRef。
3 . ActorRef 是 Actor System 創建的組件 B 對應 Actor 的一個代理。ActorRef 將消息(經過數據處理後的數據)傳輸給 Message Dispatcher 模塊。
4 . Message Dispatcher 類似於快遞的中轉站,負責接收和轉發消息。
4. Message Dispatcher 將消息(數據處理後的數據)加入組件 B 的 MailBox 隊列的隊尾。Message Dispatcher 將 MailBox 加入線程。需要注意的是,只有當 MailBox 是線程時,才能處理 MailBox 中的消息。
5. 組件 B 的 MailBox 將隊首消息(數據)取出並刪除,隊首消息交給組件 B 處理,進行模型訓練。

Actor 關鍵特徵

  • 實現了更高級的抽象。我在前面提到過,Actor 與 OOP 對象類似,封裝了狀態和行爲。但是,Actor 之間是異步通信的,多個Actor 可以獨立運行且不會被幹擾,解決了 OOP 存在的競爭問題。
  • 非阻塞性。在 Actor 模型中,Actor之間是異步通信的,所以當一個 Actor 發送信息給另外一個 Actor之後,無需等待響應,發送完信息之後可以在本地繼續運行其他任務。也就是說,Actor模型通過引入消息傳遞機制,從而避免了阻塞。
  • 無需使用鎖。Actor 從 MailBox 中一次只能讀取一個消息,也就是說,Actor內部只能同時處理一個消息,是一個天然的互斥鎖,所以無需額外對代碼加鎖。
  • 併發度高。每個 Actor 只需處理本地 MailBox的消息,因此多個 Actor 可以並行地工作,從而提高整個分佈式系統的並行處理能力。易擴展。每個 Actor 都可以創建多個Actor,從而減輕單個 Actor 的工作負載。當本地 Actor 處理不過來的時候,可以在遠程節點上啓動 Actor然後轉發消息過去。

雖然 Actor 模型有上述的諸多優點,但它並不適用於分佈式領域中所有的應用平臺或計算框架。因爲,Actor 模型還存在如下一些不足之處:

  • Actor 提供了模塊和封裝,但缺少繼承和分層,這使得即使多個 Actor 之間有公共邏輯或代碼部分,都必須在每個 Actor中重寫這部分代碼,也就是說重用性小,業務邏輯的改變會導致整體代碼的重寫。
  • Actor 可以動態創建多個 Actor,使得整個 Actor模型的行爲不斷變化,因此在工程中不易實現 Actor 模型。此外,增加 Actor 的同時,也會增加系統開銷。
  • Actor模型不適用於對消息處理順序有嚴格要求的系統。因爲在 Actor 模型中,消息均爲異步消息,無法確定每個消息的執行順序。雖然可以通過阻塞Actor 去解決順序問題,但顯然,會嚴重影響 Actor 模型的任務處理效率。

儘管 Actor 模型在需要同步處理的應用等場景具有侷限性,但它在異步場景中應用還是比較廣泛的。接下來,我們就一起看看 Actor 目前都應用在哪些地方吧。

Actor 模型的應用

  • Erlang/OTP。Erlang 是一種通用的、面向併發的編程語言,使用 Erlang 編寫分佈式應用比較簡單,而 OTP 就是Erlang 技術棧中的標準庫。Actor 模型在 Erlang 語言中得到廣泛支持和應用,其他語言的 Actor邏輯實現在一定程度上都是參照了 Erlang 的模式。實現了 Actor 模型邏輯的Erlang/OTP,可以用於構建一個開發和運行時環境,從而實現分佈式、實時的、高可用性的系統。
  • Akka。Akka 是一個爲 Java 和Scala 構建高度併發、分佈式和彈性的消息驅動應用程序的工具包。Akka 框架基於 Actor模型,提供了一個用於構建可擴展的、彈性的、快速響應的應用程序的平臺。通過使用 Actors 和 Streams 技術, Akka爲用戶提供了多個服務器,使用戶更有效地使用服務器資源並構建可擴展的系統。
  • Quasar (Java) 。Quasar 是一個開源的 JVM庫,極大地簡化了高度併發軟件的創建。Quasar 在線程實現時,參考了 Actor 模型,採用異步編程邏輯,從而爲 JVM提供了高性能、輕量級的線程,可以用在 Java 和 Kotlin 編程語言中。

總結

在這裏插入圖片描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章