天天看點

Akka筆記之消息傳遞

在akka筆記第一篇的介紹中,我們大緻介紹了下akka工具箱中的actor。在第二篇當中,我們來看一下actor消息傳遞的功能。這裡還是延用之前使用的那個學生-老師的例子。

在actor消息的第一部分中,我們會建立一個老師的actor,但學生actor則先不建立,而是使用一個叫做studentsimulatorapp的主程式。

我們現在隻考慮studentsimulatorapp發送給teacheractor的消息。這裡我所說的studentsimulatorapp指的是一個正常的主程式。

Akka筆記之消息傳遞

從圖中可以看到:

(如果有陌生的術語,沒關系,後面我們會詳細解釋的)

1. 學生建立了一個叫actorsystem的東西。

2. 他通過actorsystem來建立了一個叫actorref的對象。quoterequest消息就是發送給actorref的(它是teacheractor的一個代理)

3. actorref将消息發送給dispatcher

4. dispatcher将消息投遞到目标actor的郵箱中。

5. 随後dispatcher将mailbox扔給一個線程去執行(這點下節會重點講到)

6. mailbox将消息出隊并最終将其委托給真實的teacher actor的接收方法去處理。

正如我所說的,看不懂也别擔心。現在我們來一步步地詳細地分析下。全部講完後你可以再回過頭來看下這五個步驟。

我們用這個studentsimulatorapp來啟動jvm并初始化actorsystem。

Akka筆記之消息傳遞

從圖中可以看到,studentsimulatorapp

1. 建立了一個actorsystem

2. 通過actorsystem建立了一個teacher actor的代理(actorref)

3. 将quoterequest消息發送給代理

我們現在隻關注這三點。

actorsystem是進入到actor的世界的一扇大門。通過它你可以建立或中止actor。甚至還可以把整個actor環境給關閉掉。

另一方面來說,actor是一個分層的結構,actorsystem之于actor有點類似于java.lang.object或者scala.any的角色——也就是說,它是所有actor的根對象。當你通過actorsystem的actorof方法建立了一個actor時,你其實建立的是actorsystem下面的一個actor。

Akka筆記之消息傳遞

初始化actorsystem的代碼是這樣的:

universitymessagesystem隻是你給actorsystem起的一個可愛的名字而已。

我們來看下下面這段代碼:

actorof是actorsystem中建立actor的方法。但是正如你所看到的,它并不會傳回我們所需要的teacheractor。它傳回的是一個actorref。

這個actorref扮演了真實的actor的一個代理的角色。用戶端并不會直接和actor通信。這也正是actor模型中避免直接通路teacheractor中任何的自定義/私有方法或者變量的一種方式。

再重複一遍,消息隻會發送給actorref,最終才會到達真正的actor。你是絕對無法直接和actor進行通信的。如果你真的找到了什麼拙劣的方式來直接通信,大家會恨你入骨的。

Akka筆記之消息傳遞

還是隻有一行代碼。你隻需告訴說把quoterequest消息發送到actorref就好了。actor中的這個告訴的方式就是一個!号。(actorref中确實也有一個tell方法,不過它隻是把這個調用委托給了!号)

這就可以了!

如果你認為我在騙你的話,看一下下面studentsimulatorapp的完整代碼:

好吧,我承認我撒了點小謊。你還得關掉actorsystem,不然jvm會一直運作下去的。我還讓主線程睡眠了一小會兒,以便給點時間讓teacheractor去完成它的任務。我知道這聽起來很愚蠢。别擔心。後面我們會通過些優雅的測試用例來替換掉這種取巧的方式。

我們剛發送了一個quotemessage給actorref,但是,還壓根兒沒看着過這個消息類呢!

說曹操,曹操到:

(實踐中推薦你把消息封裝成一個好點的對象,這樣維護起來容易些)

正如你所想的那樣,quoterequest就是發送給teacheractor的那個消息。actor會回複一個quoteresponse。

actorref把消息處理功能委托給了dispatcher。實際上,當我們建立actorsystem和actorref的時候,就已經建立了一個dispatcher和mailbox了。我們來看下它們是幹什麼的。

Akka筆記之消息傳遞

每個actor都有一個mailbox(後面會介紹一種特殊的情況)。在我們這個比喻當中,每個老師也有一個郵箱。老師得去檢查郵箱并處理消息。在actor的世界中,則是另一種形式——郵箱一有機會就會要求actor去完成自己的任務。

同樣的,郵箱裡也有一個隊列來以fifo的方式來存儲并處理消息——它和實際的郵箱還有點不同,真實的郵箱新的信總是在最上面的。

dispatcher會完成一些很酷的事。從它的角度來看,它隻是從actorref中取出一條消息然後将它傳給了mailbox。但是,在這後面發生了一件不可意義的事情:

dispatcher會封裝一個executorservice(forkjoinpoll或者threadpoolexecutor)。它把mailbox扔到executorservice中去運作。

看下dispatcher裡面的一段代碼:

是的。我們看到mailbox中包含了隊列裡面的消息。由于executor得去執行mailbox,是以它得是一個thread類型。是的沒錯。mailbox的聲明及構造器就是這樣的。

下面是mailbox的簽名資訊。

Akka筆記之消息傳遞

當mailbox的run方法運作的時候,它會從隊列中取出一條消息,然後将它傳給actor去處理。

當你把消息傳給actorref的時候,最終調用的實際是目标actor裡面的一個receive方法。

teacheractor隻是一個很簡單的類,它有一個名言的清單,而receive方法很明顯就是用來處理消息的。

來看下代碼:

teacheractor的receive方法的模式比對隻會比對一種消息——quoterequest (事實上,模式比對中最好比對下預設的情況,不過這個就說來話長了)

receive方法做的就是

1. 比對quoterequest的模式

2. 從名言清單中随機選取一條

3. 構造出一個quoteresponse

4. 将quoteresponse列印到控制台上