【问题标题】:Does Akka automatically copy over variables when an actor fails当演员失败时,Akka 是否会自动复制变量
【发布时间】:2018-11-09 04:10:45
【问题描述】:

在 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
[错误] [2018 年 11 月 8 日 20:06:01.423] [监督-akka.actor.default-dispatcher-4] [akka://Supervision/user/supervisor/LifeCycleActor] null
java.lang.ArithmeticException
postRestart 中的总和为 2
postStop 中的总和是 6

如果作者说的是真的,最终的价值应该是原来的两倍。

【问题讨论】:

    标签: scala akka actor akka-supervision


    【解决方案1】:

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

    接下来解释你的代码:

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

    还有一个关于这个的描述,详情here

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

    所以,对于你的例子:

    • 当调用preRestart时,会打印老actor的总和,即2,注意:你已经inc了。
    • 调用postRestart时,打印新actor的总和,即初始值1计算sum = sum * 2,最后打印2,而不是4Inc 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官方指南冲突& 实验;而且,如果要恢复该值,我们可能不得不使用resume 而不是restart。 反正我觉得我们应该以akka官方文档为标准,理解正确的逻辑。

    【讨论】:

    • 谢谢,我修复了 Inc. 中缺失的代码。出于测试目的,我将其删除。你的回答很有帮助。
    猜你喜欢
    • 2014-01-26
    • 2018-06-15
    • 1970-01-01
    • 2016-11-26
    • 1970-01-01
    • 1970-01-01
    • 2013-08-03
    • 2013-02-06
    • 1970-01-01
    相关资源
    最近更新 更多