1

在Héctor Veiga Ortiz 的Akka Cookbook中,读者被告知

当一个actor抛出异常时,它会向主管发送一条消息,主管通过重新启动该actor来处理故障。它清除了actor的累积状态,并创建了一个新的actor,这意味着,它将最后分配给旧actor的状态的值恢复为preRestart值。

但是,我尝试测试以下代码,这表明作者所说的不是真的。

import akka.actor._
import akka.actor.SupervisorStrategy._
import akka.util.Timeout
import scala.concurrent.Await
import scala.concurrent.duration._
import akka.pattern.ask

case object Error
case class StopActor(actorRef: ActorRef)
case object Inc

class LifeCycleActor extends Actor {
  var sum = 1
  override def preRestart(reason: Throwable, message: Option[Any]):Unit =
    println(s"sum in preRestart is $sum")
  override def preStart(): Unit = println(s"sum in preStart is $sum")
  def receive = {
    case Inc => sum += 1
    case Error => throw new ArithmeticException()
    case _ => println("default msg")
  }
  override def postStop(): Unit =
    println(s"sum in postStop is ${sum * 3}")
  override def postRestart(reason: Throwable): Unit = {
    sum = sum * 2
    println(s"sum in postRestart is $sum")
  }
}

class Supervisor extends Actor {
  override val supervisorStrategy = OneForOneStrategy(maxNrOfRetries = 10, withinTimeRange = 1 minute){
    case _: ArithmeticException => Restart
    case t =>
      super.supervisorStrategy.decider.applyOrElse(t, (_:Any)=>Escalate)
  }
  def receive = {
    case (props: Props, name: String) => sender ! context.actorOf(props, name)
    case StopActor(actorRef) => context.stop(actorRef)
  }
}

object ActorLifeCycle extends App {
  implicit val timeout = Timeout(2 seconds)
  val actorSystem = ActorSystem("Supervision")
  val supervisor = actorSystem.actorOf(Props[Supervisor], "supervisor")
  val childFuture = supervisor ? (Props(new LifeCycleActor), "LifeCycleActor")
  val child = Await.result(childFuture.mapTo[ActorRef], 2 seconds)
  child ! Inc
  child ! Error
  Thread.sleep(1000)
  supervisor ! StopActor(child)
}

我得到的输出如下。

sbt:chpt2_ActorLifeCycle> runMain ActorLifeCycle preStart
中的总和是 1 preRestart 中的
总和是 2
[错误] [11/08/2018 20:06:01.423] [Supervision-akka.actor.default-dispatcher-4] [akka://Supervision /user/supervisor/LifeCycleActor] null
java.lang.ArithmeticException postRestart 中的总和
为 2 postStop 中的
总和为 6

如果作者所说的是真的,那么最终的价值应该是它的两倍。

4

1 回答 1

3

首先,我猜您在发布问题时忘记在子角色中sum += 1接收消息时添加Inc,请修改。否则,正如我测试的那样,您无法获得输出。

接下来解释你的代码:

从下图中,您可以看到preReStart在旧实例上调用,而不是在新实例上调用。

在此处输入图像描述

还有一个关于这个的描述,详细在这里

  1. 通过调用 preRestart 来通知旧参与者,其中包含导致重新启动的异常和触发该异常的消息;如果重新启动不是由处理消息引起的,则后者可能是 None ,例如,当主管没有捕获异常并由其主管依次重新启动时,或者如果由于兄弟姐妹的失败而重新启动actor。如果消息可用,那么该消息的发送者也可以以通常的方式访问(即通过调用发送者)。此方法是清理、准备移交给新的 actor 实例等的最佳场所。默认情况下,它会停止所有子节点并调用 postStop。
  2. actorOf 调用的初始工厂用于生成新实例。
  3. 新参与者的 postRestart 方法被调用,但引发了重新启动的异常。默认情况下会调用 preStart,就像在正常启动的情况下一样。

因此,对于您的示例:

  • 调用时preRestart,它打印旧演员的总和,即2注意:你有它。
  • 调用时postRestart,它打印新演员的总和,即1计算的初始值sum = sum * 2,最后打印2,而不是4。只是在旧实例上接收,而Inc message不是在新实例上。

最后是本书的内容:

当一个actor抛出异常时,它会向主管发送一条消息,主管通过重新启动该actor来处理故障。它清除了actor的累积状态,并创建了一个新的actor,这意味着,它将最后分配给旧actor的状态的值恢复为preRestart值。

我想你关心的是it then restores the last value assigned to the state of old actor to the preRestart value。我不太明白这是什么意思,如果你只是认为它将旧actor的最后一个值分配给preRestart函数,那么它是正确的,因为它只是在旧实例上运行,否则,它似乎与akka官方指南和实验冲突;而且,如果要恢复值,我们可能不得不使用resumenot restart。无论如何,我认为我们应该以akka官方文档为标准,并理解正确的逻辑。

于 2018-11-09T07:40:09.710 回答