Actor模型
Actor的概念来自于Erlang,在AKKA中,可以认为一个Actor就是一个容器,用以存储状态、行为、Mailbox以及子Actor与Supervisor策略。Actor之间并不直接通信,而是通过Mail来互通有无,就好像天各一方的一对恋人或者知交,鸿雁往来。寄出信后,虽然心里日日期盼,却也不能无所事事,还是得自己做自己的事,直到收到对方的回信,方才欣喜地抽出信笺畅怀阅读。展读之后,若觉得意犹未尽,可以再次回信。
Actor对象可以分为内部与外部,外部以引用方式传递。当我们利用ActorSystem或者ActorContext创建Actor对象时,实则返回的是ActorRef对象,它保留了对Actor对象的引用。这样的设计使得重启Actor时无需更新任何地方的引用(这是指内部的重启);也有利于将实际的Actor对象放在远端主机,从而支持分布式的Remote Actor。
Actor状态可以是显式的状态机(例如使用FSM模块,FSM即Finite State Machine,有限状态机)或者计数器、一组侦听器、待处理的请求等。从概念上讲,每个Actor都拥有属于自己的轻量级线程,保护它不会被系统的其余部分影响。我们在编写Actor时,就不用担心并发。
每个Actor都有一个(恰好一个)Mailbox。Mailbox相当于是一个小型的队列,一旦Sender发送消息,就是将该消息入队到Mailbox中。入队的顺序按照消息发送的时间顺序。Mailbox有多种实现,默认为FIFO。但也可以根据优先级考虑出队顺序,实现算法则不相同。
AKKA与其他Actor模型不同的是:当前的行为总是会处理下一个出队的消息,而不会去扫描Mailbox,获得下一个匹配的消息。因此,当处理消息失败,就会认为是失败,除非这个行为被重写了。
每个Actor都是一个潜在的Supervisor:如果该Actor创建了一个Child去执行子任务,就会自动来管理这些Child。Children的列表放在Actor的Context中,Actor可以访问他们。创建或停止的操作分别为:
context.actorOf(…)
context.stop(child)
看起来,这种变更会实时反映出来;但事实上是以异步的方式在后台执行,它并不会阻塞Supervisor。
Supervisor处理失败场景的策略在创建Actor时就被确定,因而在Actor创建之后不能改变。一个Actor只有一个策略,如果不同的策略被运用到Actor的不同Child,就会被分组,按照策略而非构建时的分类去匹配Supervisor。
一旦Actor被终止,就会释放资源。如果终止Actor时,它所持有的Mailbox中还存在未处理的消息,就会被转发给系统的“dead letter mailbox”;然后该Mailbox会被替换为系统的Mailbox。所有新发来的消息也会作为Dead letter转发到系统的Mailbox。此时,AKKA会给出如下的提示信息:
[INFO] [04/23/2015 16:49:27.776] [DataInitializer-akka.actor.default-dispatcher-3] [akka://DataInitializer/user/writer_5] Message [com.bigeyedata.mort.inception.InsertCommand] from Actor[akka://DataInitializer/temp/$a] to Actor[akka://DataInitializer/user/writer_5#-1503778427] was not delivered. [1] dead letters encountered. This logging can be turned off or adjusted with configuration settings 'akka.log-dead-letters' and 'akka.log-dead-letters-during-shutdown'.
[INFO] [04/23/2015 16:49:27.776] [DataInitializer-akka.actor.default-dispatcher-3] [akka://DataInitializer/user/writer_3] Message [com.bigeyedata.mort.inception.InsertCommand] from Actor[akka://DataInitializer/temp/$b] to Actor[akka://DataInitializer/user/writer_3#2058508180] was not delivered. [2] dead letters encountered. This logging can be turned off or adjusted with configuration settings 'akka.log-dead-letters' and 'akka.log-dead-letters-during-shutdown'.
若要保证系统的健壮性,或者方便维护人员的快速排错,需要对dead letter进行及时处理。此时,可以向Event Bus注册一个TestEventListener,监听dead letter的转发。
默认Actor
当我们创建了一个Actor System时,AKKA会为该System默认创建三个Actor,并处于不同的层次。如下图所示:
1. The Guardian Actor
它是用户创建的Actor的parent,命名为“/user”。使用system.actorOf()方法创建的Actor都是它的children。这意味着只要这个Actor终止了,系统中所有常规的Actor都会被关掉。从Akka 2.1版本后,框架提供了配置Supervisor Strategy的功能,配置项为akka.actor.guardian-supervisor-strategy,对应类名为SupervisorStrategyConfigurator。倘若这个Guardian Actor扩大了失败,按照前面描述的Supervisor策略,它会使得root guardian终止该Actor,从而使得这个Actor下的所有子Actor都停止,即关掉了整个Actor系统。
2. The System Guardian
名为“/system"。主要是为了在常规Actor被终止时,做到按序的shut-down顺序。它可以监控User Guardian。可以管理Top-Level的System Actor采用一种策略,可以在除了ActorInitializationException与ActorKilledException之外的异常出现时,无限制地重启它。
3. The Root Guardian
由于每个真正的Actor都有一个supervisor,因此,root guardian的supervisor不是一个真正的Actor。
这几个默认的Actor被定义在ActorRefProvider trait中:
trait ActorRefProvider {
def rootGuardian: InternalActorRef
def guardian: LocalActorRef
def systemGuardian: LocalActorRef
def deadLetters: ActorRef
}
这里定义的deadLetter actor则是另一个特殊的Actor,当我们向某个已经处于Terminated状态的actor发送消息时,系统就会将该消息转发给deadLetter Actor。