Actor模型與Akka Actor體系基礎(chǔ)總結(jié)
點(diǎn)擊上方藍(lán)色字體,選擇“設(shè)為星標(biāo)”

前言
最近用業(yè)余時(shí)間把Flink的RPC基礎(chǔ)設(shè)施翻了個(gè)底朝天,又與之前分析過的Spark RPC機(jī)制做了一些對(duì)比,越發(fā)覺得Actor模型甚為精妙,值得簡(jiǎn)單記錄一下,順便也可作為日后解析Flink RPC機(jī)制的基礎(chǔ)入門。
Actor模型
Actor模型由Hewitt、Bishop和Steiger在1973年通過論文《A Universal Modular Actor Formalism for Artificial Intelligence》提出,是一個(gè)創(chuàng)新的并發(fā)、分布式計(jì)算和編程模型。該模型的理念是“萬物皆Actor”,即以Actor作為最基本的功能單元,且需要遵循以下幾個(gè)基本規(guī)則。
所有的計(jì)算都是在Actor中執(zhí)行的。
Actor之間只能通過消息進(jìn)行通信,且消息是不可變的。
Actor串行處理并響應(yīng)消息。當(dāng)一個(gè)Actor響應(yīng)消息時(shí),它可以進(jìn)行下列操作:
* 更改狀態(tài)或行為;
* 發(fā)送有限數(shù)量的消息給其他Actor;
* 創(chuàng)建有限數(shù)量的子Actor。
Actor一詞在此語(yǔ)境下仍然沒有確定的中文譯名,有人把它翻譯為“角色”,大致貼切。
一個(gè)符合Actor模型的簡(jiǎn)單系統(tǒng)如下圖所示。Actor本質(zhì)上是狀態(tài)、行為、郵箱三要素的集合。

狀態(tài)(State):Actor內(nèi)部維護(hù)的變量及數(shù)據(jù)。每個(gè)Actor都單獨(dú)維護(hù)自己的狀態(tài),與其他Actor隔離。
行為(Behavior):Actor內(nèi)部定義的一組計(jì)算邏輯(如函數(shù)),用于處理接收到的消息以及改變狀態(tài)數(shù)據(jù)。
郵箱(Mailbox):可以視為與接收方Actor關(guān)聯(lián)的FIFO消息隊(duì)列。由于Actor串行處理消息,發(fā)送方發(fā)來的來不及處理的消息會(huì)存入郵箱中,接收方再?gòu)泥]箱逐條獲取pending的消息。(當(dāng)然,一個(gè)Actor既可以是發(fā)送方也可以是接收方)
可見,Actor模型另辟蹊徑解決了并發(fā)環(huán)境中最棘手的問題,即共享數(shù)據(jù)的問題。在傳統(tǒng)方案中,總需要通過同步機(jī)制(鎖、信號(hào)量、原子性內(nèi)存操作等)保證共享數(shù)據(jù)的一致性。但是同步操作的開銷都比較大,往往會(huì)拖累高并發(fā)情況下的性能表現(xiàn),并且容易引起死鎖等其他問題。而Actor模型純依賴消息傳遞,消息可以異步、非阻塞地處理,且狀態(tài)是隔離的,不需要再考慮同步,簡(jiǎn)單而高效。
當(dāng)然,Actor的結(jié)構(gòu)也很簡(jiǎn)潔,單個(gè)Actor只需利用單線程執(zhí)行,所以非常輕量級(jí),1GB的內(nèi)存可以容納上百萬的Actor實(shí)例。
Actor模型有眾多成熟的實(shí)現(xiàn),例如Erlang語(yǔ)言的并發(fā)機(jī)制就是完全基于它來實(shí)現(xiàn)的。接下來簡(jiǎn)要介紹Akka,它是目前最活躍的Actor模型開源項(xiàng)目之一,同時(shí)也是Flink RPC的基礎(chǔ)。而Spark的舊版本同樣使用Akka構(gòu)建其RPC體系,后來的新版本雖然換用了Netty,但其設(shè)計(jì)理念仍然可以近似視為簡(jiǎn)化版的Akka。
Akka Actor體系
Akka官網(wǎng)首頁(yè)的介紹如下。
高并發(fā)、分布式、彈性、消息驅(qū)動(dòng)、基于JVM,這就是Akka的五個(gè)關(guān)鍵詞,可見是深得Actor模型的精髓。
整個(gè)Akka生態(tài)分為很多庫(kù)(也叫模塊),如:Actor、Remoting、Cluster、Persistence、Streams、HTTP等。當(dāng)然,Actor庫(kù)是Akka核心中的核心,下面也僅簡(jiǎn)要總結(jié)與Actor庫(kù)相關(guān)的基礎(chǔ)知識(shí)。
Akka Actor是按照樹形層次結(jié)構(gòu)來組織的,其關(guān)系示意圖如下所示。

Akka通過Actor系統(tǒng)(ActorSystem)來管理所有Actor,每個(gè)JVM實(shí)例內(nèi)只有一個(gè)ActorSystem。當(dāng)ActorSystem啟動(dòng)時(shí),默認(rèn)有3個(gè)守護(hù)(guardian)Actor:
/:根守護(hù)Actor,如同文件系統(tǒng)中的根,最先被創(chuàng)建,最后被銷毀;
/system:系統(tǒng)守護(hù)Actor,Akka本身以及基于Akka構(gòu)建的某些模塊會(huì)在該路徑下創(chuàng)建子Actor;
/user:用戶守護(hù)Actor,我們?cè)谑褂肁kka過程中創(chuàng)建的Actor都會(huì)位于這個(gè)路徑下。當(dāng)調(diào)用ActorSystem.actorOf()方法時(shí),會(huì)在/user下直接創(chuàng)建;而當(dāng)調(diào)用某Actor的ActorContext.actorOf()方法時(shí),會(huì)在該Actor下創(chuàng)建子Actor。
創(chuàng)建或者根據(jù)路徑查找Actor,返回給用戶的都是ActorRef,可以視為Actor實(shí)例的不可變、可序列化的句柄(引用),用戶通過ActorRef來操作Actor,比如向其發(fā)送消息。
下圖示出Actor從低級(jí)到高級(jí)的三層抽象,即Actor、ActorContext和ActorRef,以及它們對(duì)應(yīng)的路徑ActorPath。

可見,Actor的實(shí)際層級(jí)關(guān)系維護(hù)在上下文實(shí)例ActorContext中(ActorContext也包含有向當(dāng)前Actor發(fā)送消息的那個(gè)ActorRef),而Actor的路徑則維護(hù)在ActorRef中。這樣就保證了從屬于不同ActorSystem的Actor之間可以正常通信。
Actor的層次結(jié)構(gòu)同時(shí)也是監(jiān)督(supervision)機(jī)制的基礎(chǔ)。當(dāng)一個(gè)Actor失敗時(shí),它會(huì)通知其父Actor采取相應(yīng)的動(dòng)作(如直接恢復(fù)、重啟、停止或者將失敗信息繼續(xù)向高層傳遞)。下圖示出一個(gè)Akka Actor的完整生命周期。

可見,Akka還提供了一些生命周期的觸發(fā)器方法(稱為hook/鉤子),用戶可以通過重寫這些方法來管理Actor的生命周期。特別需要注意的是,如果一個(gè)Actor停止,那么它的所有子Actor也會(huì)隨著停止。
最后有一個(gè)問題,整個(gè)ActorSystem是如何被驅(qū)動(dòng)的呢?答案是依靠一個(gè)中心化組件——Dispatcher(調(diào)度器/分發(fā)器),負(fù)責(zé)將Actor和與其關(guān)聯(lián)的郵箱中的消息調(diào)度到線程中進(jìn)行處理。它的原理并不難,形象的圖示如下,就不多廢話了。


版權(quán)聲明:
文章不錯(cuò)?點(diǎn)個(gè)【在看】吧!??




