Akka邊學邊寫(2)-- Echo Server

zxh0發表於2014-10-13

EchoServer

上篇文章裡,我們用Akka寫了一個簡單的HelloWorld例子,對Akka(以及Actor模式)有了初步的認識。本文將用Akka寫一個EchoServer,看看在Actor的世界裡,如何使用TCP協議

Github專案

照例,EchoServer的程式碼被放在了Github上。EchoServer比HelloWorld稍微複雜一點,一共有三個類,如下圖所示:

Main

這次先從主類入手:

main()方法的第一行建立了一個Actor系統,名字為mySystem。接下來的四行程式碼,請看下面的詳細解釋。

TCP Manager

Akka將整個TCP層抽象為一個Actor,這個Actor就是TCP Manager。在main()方法的第二行程式碼裡,我們給Actor系統新增了一個TCP Manager,並和它的經紀人取得了聯絡:

ActorRef tcpManager = Tcp.get(mySystem).getManager();
現在我們的Actor系統看起來是這樣:

Props

Props應該是Properties的縮寫,它的作用是告訴Actor系統如何建立一個Actor。Props提供了四個工廠方法來建立Props例項,如下所示:

public static Props create(Class<?> type, Object[] os)
public static <T extends Actor> Props create(Class<T> type, Creator<T> crtr)
public static <T extends Actor> Props create(Creator<T> crtr)
public static Props create(Class<?> type, Seq<Object> seq)
main()方法的第三行使用了上面的第一個工廠方法,這個工廠方法有兩個引數:一個Class,和一個陣列(實際上是vararg)。第一個參數列示Actor的class,第二個引數是傳遞給Actor建構函式的引數。這樣,Actor系統就知道如何根據Props建立(用反射呼叫建構函式)一個Actor例項。main()方法的第四行建立了一個Accepter,起名為accepter。Accepter需要用到tcpManager,這也是我們將tcpManager當做第二個引數傳遞給Props.create()方法的緣故。現在Actor系統變成了下面這樣(我用虛線箭頭來表示Actor之間的依賴關係):


main()方法的第五行給accepter發了一個訊息:整數12345。告訴它繫結埠12345,開始監聽TCP連線,準備echo服務。

Accepter

Accepter負責監聽埠,然後把收到的tcp連線交給Handler去處理。Accepter稍微有點複雜,下面是全部程式碼:

建構函式是為了建立對tcpManager的依賴,下面詳細介紹onReceive()方法:

  • 如果收到的訊息是Integer型別(port),我們給tcpManager傳送繫結訊息,通知它繫結某個埠。
  • 如果tcpManager成功繫結埠,它會迴應已繫結訊息(Bound)。
  • 否則,迴應繫結失敗訊息(CommandFailed),accepter通過呼叫getContext().stop()方法結束自己。
  • 成功繫結埠後,如果有連線到達,則會收到Connected訊息。accepter通過呼叫getContext().actorOf()建立一個Handler,然後把它註冊給tcpManager。之後tcpManager就會把與這個連線相關的訊息發給handler,換句話說,這個連線被handler接管。

Actor之間的父子關係

在Actor系統內部,Actor們之間並不是只有簡單的依賴(或引用)關係,而是可以形成父子關係:Actor可以建立子Actor,然後把子任務分配給它們去處理。一個Actor的Children是通過它的context來管理的,上面程式碼中,accepter通過getContext()來獲得它自己的context,然後通過呼叫context的actorOf()方法建立子Actor。假設現在有兩個客戶端連線到了我們的echo伺服器,那麼整個Actor系統將是下面這樣(Actor系統內的實線箭頭表示父子關係):

Handler

最後一個類是Handler,它比較簡單,程式碼如下所示:


Handler只處理兩種訊息:

  • Received訊息告訴handler有訊息到達,因為是echo伺服器,所以並不關心到達的訊息裡面是什麼內容。getSender()方法可以得到訊息的傳送者,也就是tcpManager。然後給tcpManager發一條Write訊息,告訴它把資料原封不動的寫回給客戶端。
  • ConnectionClosed告訴handler連線已經斷開,handler通過呼叫context的stop()方法結束自己短暫的生命。

測試EchoServer

啟動EchoServer,然後可以通過telnet命令進行測試,這裡就不詳細說明了。

結論

要想用Akka寫一個echo伺服器,還是挺難的,需要了解Akka的很多方面。但是相對於Socket,或者NIO來說,Akka版的echo伺服器顯然更簡單。


相關文章