Actor模型做爲Akka中最核心的概念,因此Actor在Akka中的組織結構也相當重要,本文主要介紹Akka中Actor系統。git
Actor做爲一種封裝狀態和行爲的對象,老是須要一個系統去統一的組織和管理它們,在Akka中即爲ActorSystem,其實這很是容易理解,比如一個公司,每一個員工均可以當作一個Actor,它們有本身的職位和職責,可是咱們須要把員工集合起來,統一進行管理和分配任務,因此咱們須要一個相應的系統進行管理,比如這裏的ActorSystem對Actor進行管理同樣。github
ActorSystem主要有如下三個功能:app
管理調度服務tcp
配置相關參數分佈式
日誌功能this
ActorSystem的的精髓在於將任務分拆,直到一個任務小到能夠被完整處理,而後將其委託給Actor進行處理,因此ActorSystem最核心的一個功能就是管理和調度整個系統的運行,比如一個公司的管理者,他須要制定整個公司的發展計劃,還須要將工做分配給相應的工做人員去完成,保障整個公司的正確運轉,其實這裏也體現了軟件設計中的分而治之,Actor中的核心思想也是這樣。spa
ActorSystem模型例子:插件
上圖是一個簡單的開發協做的過程,我以爲這個例子應該能夠清晰的表達Akka中Actor的組織結構,固然不只於此。主要有如下幾個特色:scala
Akka中Actor的組織是一種樹形結構設計
每一個Actor都有父級,有可能有子級固然也可能沒有
父級Actor給其子級Actor分配資源,任務,並管理其的生命狀態(監管和監控)
Actor系統每每有成千上萬個Actor,使用樹形機構來組織管理Actor是很是適合的。
並且Akka天生就是分佈式,你能夠向一個遠程的Actor發送消息,但你須要知道這個Actor的具體位置在哪,這時候你就會發現,樹形結構對於肯定一個Actor的路徑來講是很是有利(好比Linux的文件存儲),因此我以爲Actor用樹形結構組織能夠說是再完美不過了。
一個完善的ActorSystem必須有相關的配置信息,好比使用的日誌管理,不一樣環境打印的日誌級別,攔截器,郵箱等等,Akka使用Typesafe配置庫,這是一個很是強大的配置庫,後續我也準備寫一篇後續文章,你們盡請期待哈。
下面用一個簡單的例子來講明一下ActorSystem會根據配置文件內容去生成相應的Actor系統環境:
1.首先咱們按照默認配置打印一下系統的日誌級別,搭建Akka環境請看我上一篇文章:Akka系列(一):Akka簡介與Actor模型
val actorSystem = ActorSystem("robot-system") println(s"the ActorSystem logLevel is ${actorSystem.settings.LogLevel}")
運行結果:
the ActorSystem logLevel is INFO
能夠看出ActorSystem默認的日誌輸出級別是INFO
。
2.如今咱們在application.conf裏配置日誌的輸出級別:
akka { # Log level used by the configured loggers (see "loggers") as soon # as they have been started; before that, see "stdout-loglevel" # Options: OFF, ERROR, WARNING, INFO, DEBUG loglevel = "DEBUG" }
運行結果:
[DEBUG] [03/26/2017 12:07:12.434] [main] [EventStream(akka://robot-system)] logger log1-Logging$DefaultLogger started [DEBUG] [03/26/2017 12:07:12.436] [main] [EventStream(akka://robot-system)] Default Loggers started the ActorSystem logLevel is DEBUG
能夠發現咱們ActorSystem的日誌輸出級別已經變成了DEBUG
。
這裏主要是演示ActorSystem能夠根據配置文件的內容去加載相應的環境,並應用到整個ActorSystem中,這對於咱們配置ActorSystem環境來講是很是方便的。
有不少人可能會疑惑,日誌不該該只是記錄程序運行狀態和排除錯誤的嘛,怎麼在Akka中會變得相當重要,Akka擁有高容錯機制,這無疑須要完善的日誌記錄才能使Actor出錯後能及時作出相應的恢復策略,好比Akka中的持久化,具體相應的一些做用我可能會在後續寫相應章節的時候提到。
有了上面的知識,這裏瞭解Actor引用,路徑和地址就容易多了。
什麼時Actor引用?
Actor引用是ActorRef的子類,每一個Actor有惟一的ActorRef,Actor引用能夠當作是Actor的代理,與Actor打交道都須要經過Actor引用,Actor引用能夠幫對應Actor發送消息,也能夠接收消息,向Actor發送消息實際上是將消息發送到Actor對應的引用上,再由它將消息投寄到具體Actor的信箱中,因此ActorRef在整個Actor系統是一個很是重要的角色。
如何得到Actor引用?
直接建立Actor
查找已經存在的Actor
看我上一篇文章的同窗對這種方式得到Actor引用應該是比較瞭解,這裏我會具體演示一下得到ActorRef的幾種方式:
假定如今由這麼一個場景:老闆嗅到了市場上的一個商機,準備開啓一個新項目,他將要求傳達給了經理,經理根據相應的需求,來安排適合的的員工進行工做。
這個例子很簡單,如今咱們來模擬一下這個場景:
1.首先咱們來建立一些消息:
trait Message { val content: String } case class Business(content: String) extends Message {} case class Meeting(content: String) extends Message {} case class Confirm(content: String, actorPath: ActorPath) extends Message {} case class DoAction(content: String) extends Message {} case class Done(content: String) extends Message {}
2.咱們來建立一家公司,這裏就是ActorSystem的化身:
val actorSystem = ActorSystem("company-system") //首先咱們建立一家公司 //建立Actor獲得ActorRef的一種方式,利用ActorSystem.actorOf val bossActor = actorSystem.actorOf(Props[BossActor], "boss") //公司有一個Boss bossActor ! Business("Fitness industry has great prospects") //從市場上觀察到健身行業將會有很大的前景
3.這裏咱們會建立幾種角色,好比上面Boss,這裏咱們還有Manager,Worker,讓咱們來看看吧:
class BossActor extends Actor { val log = Logging(context.system, this) implicit val askTimeout = Timeout(5 seconds) import context.dispatcher var taskCount = 0 def receive: Receive = { case b: Business => log.info("I must to do some thing,go,go,go!") println(self.path.address) //建立Actor獲得ActorRef的另外一種方式,利用ActorContext.actorOf val managerActors = (1 to 3).map(i => context.actorOf(Props[ManagerActor], s"manager${i}")) //這裏咱們召喚3個主管 //告訴他們開會商量大計劃 managerActors foreach { _ ? Meeting("Meeting to discuss big plans") map { case c: Confirm => //爲何這裏能夠知道父級Actor的信息? //熟悉樹結構的同窗應該知道每一個節點有且只有一個父節點(根節點除外) log.info(c.actorPath.parent.toString) //根據Actor路徑查找已經存在的Actor得到ActorRef //這裏c.actorPath是絕對路徑,你也能夠根據相對路徑獲得相應的ActorRef val manager = context.actorSelection(c.actorPath) manager ! DoAction("Do thing") } } case d: Done => { taskCount += 1 if (taskCount == 3) { log.info("the project is done, we will earn much money") context.system.terminate() } } } } class ManagerActor extends Actor { val log = Logging(context.system, this) def receive: Receive = { case m: Meeting => sender() ! Confirm("I have receive command", self.path) case d: DoAction => val workerActor = context.actorOf(Props[WorkerActor], "worker") workerActor forward d } } class WorkerActor extends Actor { val log = Logging(context.system, this) def receive: Receive = { case d: DoAction => log.info("I have receive task") sender() ! Done("I hava done work") } }
光看這段代碼可能不那麼容易理解,這裏我會畫一個流程圖幫助你理解這段程序:
程序流程圖:
看了上面的流程圖對程序應該有所瞭解了,過多的解釋我這裏就不講解了,能夠看註釋,或者下載源代碼本身去跑一跑。源碼連接
這裏主要是有兩個知識點:
建立Actor得到ActorRef的兩種方式
根據Actor路徑得到ActorRef
前一個知識點應該比較清晰了,具體來講說第二個。
熟悉類Unix系統的同窗應該對路徑這個概念很熟悉了。ActorSystem中的路徑也很相似,每一個ActorSystem都有一個根守護者,用/
表示,在根守護者下有一個名user的Actor,它是全部system.actorOf()建立的父Actor,因此咱們程序中bossActor的路徑爲:
/user/boss
地址顧名思義是Actor所在的位置,爲何要有地址這一個概念,這就是Akka強大的理念了,Akka中全部的東西都是被設計爲在分佈式環境下工做的,因此咱們能夠向任意位置的Actor發送消息(前提你得知道它在哪),這時候地址的做用就顯現出來來,首先咱們能夠根據地址找到Actor在什麼位置,再根據路徑找到具體的Actor,好比咱們示例程序中bossActor,它的完整位置是
akka://company-system/user/boss
能夠發現它的地址是
akka://company-system
其中akka表明純本地的,Akka中默認遠程Actor的位置通常用akka.tcp或者akka.udp開頭,固然你也可使用第三方插件,Akka的遠程調用我也會專門寫一篇文章。
總的來講這一篇文章主要是講解了ActorSystem的基礎結構,相關配置,以及Actor引用,路徑和地址等比較基礎的知識點,這其實對理解整個Actor系統是如何運行的是頗有幫助的,博主也是寫了很久,爭取寫的通俗容易理解一點,但願能獲得你們的支持,下一篇準備寫一下Actor的監管和監控以及它的生命週期。有興趣的同窗也能夠關注個人我的博客