死信箱是否可在 akka 中重复使用?

Is dead letter box reusable in akka?

我编写了以下代码:

package com.star.wars

import akka.actor._
import akka.actor.SupervisorStrategy._
import akka.util.duration._

object Test extends App {

  case object Kill
  case object Create

  class Luke extends Actor {

    var x: Int = 0

    println("Luke here")
    Thread.sleep(1000)
    println("Luke here2")

    def receive = {
      case Kill => 1/0// context.stop(self)
      case msg: String => {
        x += 1
        println(x + msg)
      }
    }

  }

  class Vader extends Actor {

    println("Vader here")

    override val supervisorStrategy =
      OneForOneStrategy(maxNrOfRetries = 10, withinTimeRange = 1 minute) {
        case _: ArithmeticException      => Restart
        case _: NullPointerException     => Restart
        case _: IllegalArgumentException => Restart
        case _: Exception                => Restart
      }

    def receive = {
      case Create => context.actorOf(Props(new Luke), name = "Luke")
      case Kill => {
        val luke = context.actorFor("/user/Vader/Luke")
        luke ! "Pre hi there"
        luke ! Kill
        luke ! "Post hi there"
        println("Pre -> Kill -> Post sent to Luke")
      }
    }

  }

  val system = ActorSystem("MySystem")
  val vader = system.actorOf(Props(new Vader), name = "Vader")
  vader ! Create
  vader ! Kill
  println("Create -> Kill sent to Vader")

}

这段代码的目的,是为了证明,当路克重启时,他的死信箱可以接收消息,当路克再次上线时,他可以接收到他不在时发给他的消息。

输出似乎没问题。这在某种程度上是一种证明:

Create -> Kill sent to Vader
Vader here
Luke here
Pre -> Kill -> Post sent to Luke
Luke here2
1Pre hi there
[ERROR] [01/12/2015 00:32:02.74] [MySystem-akka.actor.default-dispatcher-3] [akka://MySystem/user/Vader/Luke] / by zero
java.lang.ArithmeticException: / by zero
    at com.sconysoft.robocode.Test$Luke$$anonfun$receive.apply(test.scala:21)
    at com.sconysoft.robocode.Test$Luke$$anonfun$receive.apply(test.scala:20)
    at akka.actor.Actor$class.apply(Actor.scala:318)
    at com.sconysoft.robocode.Test$Luke.apply(test.scala:12)
    at akka.actor.ActorCell.invoke(ActorCell.scala:626)
    at akka.dispatch.Mailbox.processMailbox(Mailbox.scala:197)
    at akka.dispatch.Mailbox.run(Mailbox.scala:179)
    at akka.dispatch.ForkJoinExecutorConfigurator$MailboxExecutionTask.exec(AbstractDispatcher.scala:516)
    at akka.jsr166y.ForkJoinTask.doExec(ForkJoinTask.java:259)
    at akka.jsr166y.ForkJoinPool$WorkQueue.runTask(ForkJoinPool.java:975)
    at akka.jsr166y.ForkJoinPool.runWorker(ForkJoinPool.java:1479)
    at akka.jsr166y.ForkJoinWorkerThread.run(ForkJoinWorkerThread.java:104)

Luke here
Luke here2
1Post hi there

但是,我不确定它是否总是真实的。我在 akka 文档中找不到它,所以我的推理可以吗?死信箱是否可在 akka 中重复使用(在 actor 重启后)?

顺便说一句。我怎样才能在维达的 supervisorStrategy 中处理卢克的 context.stop(self)

这与死信无关,请参阅Message Delivery Reliability. In Akka, when using In-JVM-messaging - you have guarantee that message will be delivered with high-probability in most cases (restarting after exception is one of those). Messages that could not be delivered (like messages to voluntary stopped or never existed actor) are going to the DeadLetters box, but you should subscribe to them explicitly, which is not you've got here. You had just received the messages from your own actor's mailbox (as box wasn't removed during restarting - only actor's instance). You need to explicitly subscribe to the corresponding Event Stream观看死信。

您不能在 supervisorStrategy 中处理 context.stop(self),因为它是自愿终止(这实际上会导致消息进入 deadLetters)而不是异常情况(失败)。所以 1/0context.stop(self) 是非常不同的。要收听 child 的生命周期,请参阅 - What Lifecycle Monitoring Means

例如,让我们看看如果您真的将context.stop(self)而不是1/0放入代码中会怎样:

Luke here
Pre -> Kill -> Post sent to Luke
Luke here2
1Pre hi there
[INFO] [01/12/2015 09:20:37.325] [MySystem-akka.actor.default-dispatcher-4] [akka://MySystem/user/Vader/Luke] Message [java.lang.String] from Actor[akka://MySystem/user/Vader#-1749418461] to Actor[akka://MySystem/user/Vader/Luke#-1436540331] 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'.

这就是("blabla was not delivered" 在日志记录中)使用 deadLetters 邮箱的地方。

无论如何,任何传递(包括 dead-letters 因为它只是一个合成演员)都是基于 最大努力 原则 in-JVM 消息传递,所以有没有绝对保证:

The Akka test suite relies on not losing messages in the local context (and for non-error condition tests also for remote deployment), meaning that we actually do apply the best effort to keep our tests stable. A local tell operation can however fail for the same reasons as a normal method call can on the JVM:

  • WhosebugError
  • OutOfMemoryError
  • other VirtualMachineError

In addition, local sends can fail in Akka-specific ways:

  • if the mailbox does not accept the message (e.g. full BoundedMailbox)
  • if the receiving actor fails while processing the message or is already terminated

While the first is clearly a matter of configuration the second deserves some thought: the sender of a message does not get feedback if there was an exception while processing, that notification goes to the supervisor instead. This is in general not distinguishable from a lost message for an outside observer.

在 network-messaging 的情况下,您根本没有交货保证。卢克永远不知道谁是他的父亲。为什么?因为它更快而且实际上 Nobody Needs Reliable Messaging:

The only meaningful way for a sender to know whether an interaction was successful is by receiving a business-level acknowledgement message, which is not something Akka could make up on its own (neither are we writing a “do what I mean” framework nor would you want us to).