Actor的生命周期

AKKA为Actor生命周期的每个阶段都提供了一个钩子(hook)方法,我们可以通过观察自定义Actor需要重写的方法来理解Actor的生命周期。如下图所示:

重写Actor方法

Actor被定义为trait,其中一个典型的方法对是preStart()与postStop(),顾名思义,两个方法分别在启动和停止时被调用。在Actor trait中,它们被定义为两个空实现:

trait Actor {
  @throws(classOf[Exception]) // when changing this you MUST also change UntypedActorDocTest
  //#lifecycle-hooks
  def preStart(): Unit = ()

  //#lifecycle-hooks

  /**
   * User overridable callback.
   * <p/>
   * Is called asynchronously after 'actor.stop()' is invoked.
   * Empty default implementation.
   */
  @throws(classOf[Exception]) // when changing this you MUST also change UntypedActorDocTest
  //#lifecycle-hooks
  def postStop(): Unit = ()

  //#lifecycle-hooks
}

当然,在Scala中,一个对象的创建总是从构造函数开始的。故而,一个Actor的生命周期依次为:

actorOf -> preStart -> start -> receive -> stop -> postStop

其中actorOf可以控制Actor的创建,它被定义在ActorRefFactory trait中,并分别被ActorSystem与ActorContext实现。在AKKA的早期版本中,提供了actorFor()方法,如今已被标记为deprecated。Remote Actor的创建则有所不同,需要知道该Actor的Path,故而由actorSelection()方法完成。具体关于Actor的创建(准确说来,是ActorRef对象),会在下一节详细介绍。

与之对应的是stop,控制的方式除了显式调用ActorSystem或ActorContext的stop()方法外,还可以通过发送PoisonPill或Kill消息来完成,这几种方式的区别同样留至下一节。

在使用Actor时,其实我们从未察觉到start()方法的存在。这是因为AKKA将其设计为:一旦创建了Actor,就会自动启动,且这种启动方式是异步的。

然而,上述Actor的生命周期不过是一种美丽的假象,正常情况下,我们期待的Actor从诞生到消亡的人生正是如此平淡无奇。但谁的人生不泛起一点点波澜呢?Actor似乎为了体现人生百态,也会怄气,也会撒娇,消极怠工甚至干脆撂挑子罢工。而Actor的控制者,例如ActorContext,或者Actor的supervisior也可能会拿出家长风范,会强行干涉Actor的生活。故而在上图中,我们通过preRestart()与postRestart()这一对方法,可以猜测到Actor可能会处于restart状态。

作为生命周期Restart状态的钩子方法,preRestart()默认行为是在重启(restarting)之前,它会终止所有的children actors(这个过程是递归的)。postRestart()则发生在重启成功之后。当然,方法都可以重写这两个方法以改变其行为。

其实对于Actor的生命周期,在AKKA内部,是通过InternalActorRef来控制的,定义在其中的方法庶几可以透露生命周期管理的信息:

private[akka] abstract class InternalActorRef extends ActorRef with ScalaActorRef { this: ActorRefScope/*
   * Actor life-cycle management, invoked only internally (in response to user requests via ActorContext).
   */
  def start(): Unit
  def resume(causedByFailure: Throwable): Unit
  def suspend(): Unit
  def restart(cause: Throwable): Unit
  def stop(): Unit
}

看到这些方法,是否有似曾相识之感呢?Thread的操作与之非常相似。

Akka官方文档提供了说明Actor生命周期的图片,如下所示: Actor的生命周期