Akka故障处理和调度程序

Akka故障处理和调度程序,akka,scheduler,Akka,Scheduler,当我在Actor的构造函数中调度消息,并且Actor在消息发送之前失败(异常)时,会发生什么 当actor恢复或重新启动时,消息是否会像什么都没有发生一样发送 当演员停止时,消息会发送到死信箱吗 当我在preStart()中启动计时器时,当参与者在故障后重新启动时,是否会有两条预定消息 您的问题的答案如下: 1) 是的,只要您使用以ActorRef为参数的调度器scheduleOnce变量,参与者就会收到消息。由于ActorRef只是一个基于参与者地址的轻量级代理,因此只要它成功地重新启动备份

当我在Actor的构造函数中调度消息,并且Actor在消息发送之前失败(异常)时,会发生什么

  • 当actor恢复或重新启动时,消息是否会像什么都没有发生一样发送
  • 当演员停止时,消息会发送到死信箱吗
  • 当我在
    preStart()
    中启动计时器时,当参与者在故障后重新启动时,是否会有两条预定消息

    • 您的问题的答案如下:

      1) 是的,只要您使用以
      ActorRef
      为参数的调度器
      scheduleOnce
      变量,参与者就会收到消息。由于
      ActorRef
      只是一个基于参与者地址的轻量级代理,因此只要它成功地重新启动备份并重新绑定到
      ActorRef
      表示的地址,它就可以在目标参与者失败后生存,并仍然将消息路由到它/

      2) 是的,如果
      ActorRef
      用于
      ActorSystem
      中不再表示的路径,则消息将改为发送到死信

      3) 是的,你会的。如果您在
      pretart
      中或在actor(构造函数)的主体中执行此操作,并且actor失败并重新启动,那么计划的actor现在将为同一
      ActorRef
      执行两个作业,因此最终将收到两个请求

      一个小的代码来显示所有这些在行动。考虑以下演员:

      class TestSchedActor extends Actor{
        import context._
      
        override def preStart = {
          context.system.scheduler.scheduleOnce(1 second, self, "bar")  
        }      
      
        def receive = {
      
          case "foo" =>
            val s:String = null
            s.length
      
          case "baz" =>
            context stop self
      
          case other =>
            println(s"Got $other")
        }
      }
      
      如果以这种方式进行测试:

        val system = ActorSystem("schedtest")
        val ref = system.actorOf(Props[TestSchedActor])
        ref ! "foo"
      
      那么输出将是:

      [ERROR] [04/03/2014 07:58:24.988] [schedtest-akka.actor.default-dispatcher-2] [   akka://schedtest/user/$a] null
      java.lang.NullPointerException
      at code.TestSchedActor$$anonfun$receive$1.applyOrElse(Asking.scala:27)
      at akka.actor.ActorCell.receiveMessage(ActorCell.scala:498)
      at akka.actor.ActorCell.invoke(ActorCell.scala:456)
      at akka.dispatch.Mailbox.processMailbox(Mailbox.scala:237)
      at akka.dispatch.Mailbox.run(Mailbox.scala:219)
      at akka.dispatch.ForkJoinExecutorConfigurator$AkkaForkJoinTask.exec(AbstractDispatcher.scala:386)
      at scala.concurrent.forkjoin.ForkJoinTask.doExec(ForkJoinTask.java:262)
      at scala.concurrent.forkjoin.ForkJoinPool$WorkQueue.runTask(ForkJoinPool.java:975)
      at scala.concurrent.forkjoin.ForkJoinPool.runWorker(ForkJoinPool.java:1478)
      at scala.concurrent.forkjoin.ForkJoinWorkerThread.run(ForkJoinWorkerThread.java:104)
      
      Got bar
      Got bar
      
      这显示了#1和#3,因为参与者在失败后仍然收到消息,并且在重新启动消息时再次重新调度消息时实际收到了2

      如果你像这样测试演员:

        val system = ActorSystem("schedtest")
        val ref = system.actorOf(Props[TestSchedActor])
        ref ! "baz" 
      
      然后您将看到以下输出:

      [INFO] [04/03/2014 08:01:14.199] [schedtest-akka.actor.default-dispatcher-2] [akka://schedtest/user/$a] Message [java.lang.String] from 
      Actor[akka://schedtest/user/$a#687201705] to Actor[akka://schedtest/user/$a#687201705] 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'.
      

      如果您没有禁用死信日志记录。

      您的问题的答案如下:

      1) 是的,只要您使用以
      ActorRef
      为参数的调度器
      scheduleOnce
      变量,参与者就会收到消息。由于
      ActorRef
      只是一个基于参与者地址的轻量级代理,因此只要它成功地重新启动备份并重新绑定到
      ActorRef
      表示的地址,它就可以在目标参与者失败后生存,并仍然将消息路由到它/

      2) 是的,如果
      ActorRef
      用于
      ActorSystem
      中不再表示的路径,则消息将改为发送到死信

      3) 是的,你会的。如果您在
      pretart
      中或在actor(构造函数)的主体中执行此操作,并且actor失败并重新启动,那么计划的actor现在将为同一
      ActorRef
      执行两个作业,因此最终将收到两个请求

      一个小的代码来显示所有这些在行动。考虑以下演员:

      class TestSchedActor extends Actor{
        import context._
      
        override def preStart = {
          context.system.scheduler.scheduleOnce(1 second, self, "bar")  
        }      
      
        def receive = {
      
          case "foo" =>
            val s:String = null
            s.length
      
          case "baz" =>
            context stop self
      
          case other =>
            println(s"Got $other")
        }
      }
      
      如果以这种方式进行测试:

        val system = ActorSystem("schedtest")
        val ref = system.actorOf(Props[TestSchedActor])
        ref ! "foo"
      
      那么输出将是:

      [ERROR] [04/03/2014 07:58:24.988] [schedtest-akka.actor.default-dispatcher-2] [   akka://schedtest/user/$a] null
      java.lang.NullPointerException
      at code.TestSchedActor$$anonfun$receive$1.applyOrElse(Asking.scala:27)
      at akka.actor.ActorCell.receiveMessage(ActorCell.scala:498)
      at akka.actor.ActorCell.invoke(ActorCell.scala:456)
      at akka.dispatch.Mailbox.processMailbox(Mailbox.scala:237)
      at akka.dispatch.Mailbox.run(Mailbox.scala:219)
      at akka.dispatch.ForkJoinExecutorConfigurator$AkkaForkJoinTask.exec(AbstractDispatcher.scala:386)
      at scala.concurrent.forkjoin.ForkJoinTask.doExec(ForkJoinTask.java:262)
      at scala.concurrent.forkjoin.ForkJoinPool$WorkQueue.runTask(ForkJoinPool.java:975)
      at scala.concurrent.forkjoin.ForkJoinPool.runWorker(ForkJoinPool.java:1478)
      at scala.concurrent.forkjoin.ForkJoinWorkerThread.run(ForkJoinWorkerThread.java:104)
      
      Got bar
      Got bar
      
      这显示了#1和#3,因为参与者在失败后仍然收到消息,并且在重新启动消息时再次重新调度消息时实际收到了2

      如果你像这样测试演员:

        val system = ActorSystem("schedtest")
        val ref = system.actorOf(Props[TestSchedActor])
        ref ! "baz" 
      
      然后您将看到以下输出:

      [INFO] [04/03/2014 08:01:14.199] [schedtest-akka.actor.default-dispatcher-2] [akka://schedtest/user/$a] Message [java.lang.String] from 
      Actor[akka://schedtest/user/$a#687201705] to Actor[akka://schedtest/user/$a#687201705] 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'.
      

      假设您没有禁用死信日志记录。

      我假设您的参与者(使用计划任务)向自己发送消息(使用类似system.actorSelection的内容来解析自己的地址)。 然后: 1) 对,; 2) 对,; 3) 是(此外,如果您在构造函数中启动计时器,您将获得相同的行为)


      为了避免所有这些问题,可以在preStart()中启动计时器,将收到的cancelable保存到Actor内部的局部变量中,然后在postStop()中取消它。postStop()/preStart()是从preRestart()/poststart()调用的,因此计划的任务将在Actor重新启动时重新安排,并在Actor停止时取消。

      我假设您的Actor(使用计划的任务)向自身发送消息(使用类似system.actorSelection的方法来解析自身地址)。 然后: 1) 对,; 2) 对,; 3) 是(此外,如果您在构造函数中启动计时器,您将获得相同的行为)


      为了避免所有这些问题,可以在preStart()中启动计时器,将收到的cancelable保存到Actor内部的局部变量中,然后在postStop()中取消它。postStop()/preStart()是从preRestart()/poststart()调用的,因此计划的任务将在Actor重新启动时重新安排,并在Actor停止时取消。

      感谢这两个答案以及大量详细信息:)。我不知道一个
      ActorRef
      可以“生存”失败和其他东西。谢谢你的两个答案和大量详细信息:)。我不知道一个
      ActorRef
      可以“生存”失败和其他东西。