Akka 是一個用 Scala 編寫的庫,用於簡化編寫容錯的、高可伸縮性的 Java 和 Scala 的 Actor 模型應用。
Actor模型並非什么新鮮事物,它由Carl Hewitt於上世紀70年代早期提出,目的是為了解決分布式編程中一系列的編程問題。其特點如下:
- 系統中的所有事物都可以扮演一個Actor
- Actor之間完全獨立
- 在收到消息時Actor所采取的所有動作都是並行的,在一個方法中的動作沒有明確的順序
- Actor由標識和當前行為描述
- Actor可能被分成原始(primitive)和非原始(non primitive)類別
- 非原始Actor有
- 由一個郵件地址表示的標識
- 當前行為由一組知識(acquaintances)(實例變量或本地狀態)和定義Actor在收到消息時將采取的動作組成
- 消息傳遞是非阻塞和異步的,其機制是郵件隊列(mail-queue)
- 所有消息發送都是並行的
首先通過一個簡單的例子快速簡單的介紹AKKA actor 是如何實現和使用的。
創建簡單的 Actor 模式應用程序,定義EchoServer如下代碼所示,EchoServer類繼承 AKKA 中的 Actor 類,定義偏函數(Partial Function)receive,receive 函數中通過模式匹配(Pattern Match)實現程序邏輯。
1 package foo 2 3 import akka.actor.{ Actor, ActorSystem, Props} 4 import akka.event.Logging 5 6 object test extends App { 7 8 val system = ActorSystem() 9 10 class EchoServer extends Actor { 11 val log = Logging(context.system,this) 12 def receive = { 13 case msg: String => println("echo " + msg) 14 } 15 } 16 17 val echoServer = system.actorOf(Props[EchoServer]) 18 echoServer ! "hello!"
如上代碼所示,通過 AKKA 中的 ActorSystem 對象的 actorOf 方法創建上面的 EchoServer類對象實例,返回AKKA 中的 ActorRef 類型的 EchoServer對象, echoServer對象是 EchoServer類對象實例的引用,通過 echoServer對象可以向 EchoServer類對象實例發送消息。
注意:
- 如果 receive 方函數中不存在默認匹配,則會向 ActorSystem 的事件消息流(Event Stream)發送akka.actor.UnhandledMessage(message,sender,recipient)消息。
- ActorRef 類型的對象是不可變的和可序列化的,可以在網絡中進行傳輸,作為遠程對象使用,具體的操作還是在本地的 Actor 類對象。
- Actor 對象的名稱可以在創建時省略;如果不省略 actor 對象的名稱,那么在同一個父 actor 對象下子actor 的名稱必須唯一。
- Actor 對象的名稱不能為空,並且不能是以‘$’開頭的字符串。
創建帶參數構造器 Actor
1 package foo 2 3 import akka.actor.{ Actor, ActorSystem, Props} 4 import akka.event.Logging 5 6 object test extends App { 7 8 val system = ActorSystem() 9 10 class Actor2(name:String) extends Actor { 11 val log = Logging(context.system,this) 12 def receive = { 13 case "hello" => log.info(name + " echo hello " ) 14 case _ => log.info(name + " unknown msg") 15 } 16 } 17 val actor2 = system.actorOf(Props(new Actor2("tom"))) 18 19 actor2 ! "hello"
如上面代碼所示,Actor2 類帶有參數的構造函數,這種情況下無法使用Props[Actor2]的方式創建 actor2 對象。可以采用 call-by-name塊(參考scala相關內容)的方式創建 actor2 對象
注意:
- Props(...)中不能始終傳入同一個 actor 對象,例如 val lazy 或 object extends Actor 等單例工廠實現方式,這會和 AKKA 中 actor restart 機制沖突。
- 構造器參數不能是可變的(var),因為 call-by-name 塊可能被其它線程調用,引起條件競爭。
Actor API
Actor trait 只定義了一個抽象方法,就是上面提到的 receive, 用來實現actor的行為。
如果當前 actor 的行為與收到的消息不匹配,則會調用 unhandled, 它的缺省實現是向actor系統的事件流中發布一條 akka.actor.UnhandledMessage(message, sender, recipient)。
另外,它還包括:
- self 代表本actor的 ActorRef
- sender 代表最近收到的消息的發送actor,通常用於下面將講到的 回應消息中
- supervisorStrategy 用戶可重寫它來定義對子actor的監管策略
- context 暴露actor和當前消息的上下文信息
