【问题标题】:Child actor Termination validation子演员终止验证
【发布时间】:2015-01-11 17:05:02
【问题描述】:

我有以下演员层次结构父 -> 子 -> 工人 孩子的生活范围很紧,要求 - 当请求完成时,孩子演员应该被终止。我想作为测试的一部分进行验证。我创建 StepParent 作为测试目的,因为我想验证给定请求的响应,该响应转发到 msg 到 testprobe。

class StepParent(child: Props, name: String, probe: ActorRef) extends Actor with ActorLogging {
  context.watch(context.actorOf(props = child, name = name))

  override def receive: Actor.Receive = {
    case msg: PersistImages => context.children.foreach(child => child.tell(msg, sender))
    case msg =>
      log.debug(s"Msg forwarded to probe $msg")
      probe.tell(msg, sender)
  }
}

我的测试如下:

class ImagesControllerActorTest extends TestKit(ActorSystem("testsystem"))
with WordSpecLike with MustMatchers with StopSystemAfterAll {

  val id = "456"

  "ControllerActor" must {
    "distribute a work to dedicated dedicated workers and combine back results and then terminate" in {

      val p = TestProbe()
      val ica = system.actorOf(Props(classOf[StepParent], createActorWithMockedWorkers(id, p.ref), "ControllerActor", p.ref), "parent")

      p.send(ica, PersistImages(Set(new URL("http://success"), new URL("http://fail"))))

      p.expectMsgPF(2 seconds)(validMsgPersistImageActor)
      p.expectMsgPF(2 seconds)(validMsgPersistImageActor)

      p.expectMsg(2 seconds, ImagesProcessed(id, Set(new URI("file:/"))))

      p.expectMsg(4 seconds, Terminated)
    }
  }

由于上次检查预期消息,我的测试失败:

assertion failed: timeout (4 seconds) during expectMsg while waiting for Terminated
java.lang.AssertionError: assertion failed: timeout (4 seconds) during expectMsg while waiting for Terminated
    at scala.Predef$.assert(Predef.scala:179)
    at akka.testkit.TestKitBase$class.expectMsg_internal(TestKit.scala:338)
...

根据详细日志,Terminated msg 也被转发(根据最后一行)

2015-01-11 17:41:10,386 [WARN ] [testsystem-akka.actor.default-dispatcher-5] akka.tcp://testsystem@127.0.0.1:2555/user/parent/ControllerActor - id: 456 image url: http://fail FAILED
2015-01-11 17:41:10,386 [INFO ] [testsystem-akka.actor.default-dispatcher-5] akka.tcp://testsystem@127.0.0.1:2555/user/parent/ControllerActor - id: 456 Processing completed with 1 downloded and 1 failed
2015-01-11 17:41:10,387 [DEBUG] [testsystem-akka.actor.default-dispatcher-4] akka.tcp://testsystem@127.0.0.1:2555/user/parent - Msg forwarded to probe ImagesProcessed(456,Set(file:/))
2015-01-11 17:41:10,392 [DEBUG] [testsystem-akka.actor.default-dispatcher-2] akka://testsystem/user/parent/ControllerActor/$b - stopped
2015-01-11 17:41:10,394 [DEBUG] [testsystem-akka.actor.default-dispatcher-5] akka://testsystem/user/parent/ControllerActor - stopping
2015-01-11 17:41:10,396 [INFO ] [testsystem-akka.actor.default-dispatcher-4] akka://testsystem/user/parent/ControllerActor/$b - Message [akka.dispatch.sysmsg.Terminate] from Actor[akka://testsystem/user/parent/ImagesControllerActor/$b#-426862126] to Actor[akka://testsystem/user/parent/ControllerActor/$b#-426862126] 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'.
2015-01-11 17:41:10,396 [INFO ] [testsystem-akka.actor.default-dispatcher-2] akka://testsystem/user/parent/ControllerActor/$a - Message [akka.dispatch.sysmsg.Terminate] from Actor[akka://testsystem/user/parent/ControllerActor/$a#1067345522] to Actor[akka://testsystem/user/parent/ControllerActor/$a#1067345522] was not delivered. [2] 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'.
2015-01-11 17:41:10,398 [DEBUG] [testsystem-akka.actor.default-dispatcher-2] akka://testsystem/user/parent/ControllerActor - stopped
2015-01-11 17:41:10,399 [DEBUG] [testsystem-akka.actor.default-dispatcher-16] akka://testsystem/user/parent/ControllerActor/$a - stopped
2015-01-11 17:41:10,399 [DEBUG] [testsystem-akka.actor.default-dispatcher-5] akka://testsystem/user/parent - received AutoReceiveMessage Envelope(Terminated(Actor[akka://testsystem/user/parent/ControllerActor#-770422232]),Actor[akka://testsystem/user/parent/ControllerActor#-770422232])
**2015-01-11 17:41:10,400 [DEBUG] [testsystem-akka.actor.default-dispatcher-5] akka.tcp://testsystem@127.0.0.1:2555/user/parent - Msg forwarded to probe Terminated(Actor[akka://testsystem/user/parent/ControllerActor#-770422232])**
...

我真的不明白为什么最后一次expectedMsg 检查在这里不起作用,因为消息像往常一样转发。自动收到的消息有什么特殊处理吗?

有人可以请一些棚子吗?

谢谢

更新: 尝试按照建议解决该问题 - 删除包装信封,如下所示:

class StepParent(child: Props, name: String, probe: ActorRef) extends Actor with ActorLogging {
  context.watch(context.actorOf(props = child, name = name))

  override def receive: Actor.Receive = {
    case msg: PersistImages => context.children.foreach(child => child.tell(msg, sender))
    case mssg: Envelope =>
      log.debug(s"Envelope msg forwarded to probe $mssg")
      probe.tell(mssg.message, sender)
    case msg =>
      log.debug(s"Msg forwarded to probe $msg")
      probe.tell(msg, sender)
  }
}

2015-01-11 23:52:16,352 [DEBUG] [testsystem-akka.actor.default-dispatcher-4] akka://testsystem/user/parent/ControllerActor - stopping
2015-01-11 23:52:16,354 [DEBUG] [testsystem-akka.actor.default-dispatcher-4] akka://testsystem/user/parent/ControllerActor - stopped
2015-01-11 23:52:16,358 [DEBUG] [testsystem-akka.actor.default-dispatcher-16] akka.tcp://testsystem@127.0.0.1:2555/user/parent - Msg forwarded to probe ImagesProcessed(456,Set(file:/))
2015-01-11 23:52:16,358 [DEBUG] [testsystem-akka.actor.default-dispatcher-16] akka://testsystem/user/parent - received AutoReceiveMessage Envelope(Terminated(Actor[akka://testsystem/user/parent/ControllerActor#-1965336139]),Actor[akka://testsystem/user/parent/ControllerActor#-1965336139])
2015-01-11 23:52:16,360 [DEBUG] [testsystem-akka.actor.default-dispatcher-16] akka.tcp://testsystem@127.0.0.1:2555/user/parent - Msg forwarded to probe Terminated(Actor[akka://testsystem/user/parent/ControllerActor#-1965336139])
2015-01-11 23:52:16,365 [DEBUG] [testsystem-akka.actor.default-dispatcher-16] akka://testsystem/system/testActor2 - received AutoReceiveMessage Envelope(Terminated(Actor[akka://testsystem/user/parent/ControllerActor#-1965336139]),Actor[akka://testsystem/user/parent/ControllerActor#-1965336139])

它仍然失败,似乎这里发生了一些可疑的事情:

2015-01-11 23:52:16,360 [DEBUG] [testsystem-akka.actor.default-dispatcher-16] akka.tcp://testsystem@127.0.0.1:2555/user/parent - Msg forwarded to probe Terminated(Actor[akka://testsystem/user/parent/ImagesControllerActor#-1965336139])

这不是来自 StepParent 的正确消息,但它以某种方式被提取。

【问题讨论】:

  • 当您在context.children.foreach(child => child.tell(msg, sender)) 中使用它时,我不确定sender 是否始终在范围内。你可以尝试在actor中为它创建一个引用,比如:case msg: PersistImages => val current_sender = sender; context.children.foreach(child => child.tell(msg, current_sender))
  • @Ashalynd 好建议,但这也无济于事。与之前的消息相比,更多信息完全通过相同的路径发送并成功。

标签: scala akka akka-testkit


【解决方案1】:

StepParent 实际上收到了Terminated 消息的Envelope

你可以从日志中看到:

2015-01-11 17:41:10,399 [DEBUG] [testsystem-akka.actor.default-dispatcher-5] akka://testsystem/user/parent - received AutoReceiveMessage Envelope(Terminated(Actor[akka://testsystem/user/parent/ControllerActor#-770422232]),Actor[akka://testsystem/user/parent/ControllerActor#-770422232])

然而,Envelope 消息随后会打印在您的日志中,就好像它是 Terminated 而不是。

消息是Envelope,以便包含来自自动发送消息的发件人元数据,即使在这种情况下,Terminated 消息已经包含发件人信息。

因此,要通过测试,您可以执行以下操作:

override def receive: Actor.Receive = {
  case msg: PersistImages => context.children.foreach(child => child.tell(msg, sender))
  case msg: Envelope =>
    log.debug(s"Envelope msg forwarded to probe $msg")
    probe.tell(msg.message, sender)
  case msg =>
    log.debug(s"Msg forwarded to probe $msg")
    probe.tell(msg, sender)
}

【讨论】:

  • 这里似乎有些可疑之处,因为仅移除包装对象信封没有帮助
【解决方案2】:

您的终止断言不正确。按照目前的编码:

p.expectMsg(4 seconds, Terminated)

您基本上是在说您期待的是 Terminated 类型本身的消息,而不是 Terminated 案例类的实例。您应该将断言更改为:

p.expectMsg(4 seconds, Terminated(refToBeTerminated))

或者更好:

p.expectTerminated(refToBeTerminated, 4 seconds)

其中refToBeTerminated 是您希望终止的ActorRef。我不确定这是否是您唯一的问题,但这是肯定的问题。

编辑

当然,如果您只关心您有任何类型的Terminate,那么您有多种选择来测试它。你可以试试:

p.expectMsgType[Terminated](4 seconds)

或:

p.expectMsgClass(4 seconds, classOf[Terminated])

甚至:

p.expectMsgPF(4 seconds){case t:Terminated => }

【讨论】:

  • 我希望这样可以避免需要指定我没有的孩子的 ActorRef 并且它不容易在外部观察到。我对 Terminated 的内容不是特别感兴趣
  • 不幸的是,即使我看到他们是通过自动接收日志接收到的,也没有一个有效
【解决方案3】:

不幸的是,尽管我也确信它们可以工作并且从自动接收日志中传递了已终止消息,但不幸的是,提议的解决方案都不起作用,我让这个东西工作的唯一方法是一种“丑陋”的解决方案,将 Terminated 消息翻译为别的东西:

class StepParent(child: Props, name: String, probe: ActorRef) extends Actor with ActorLogging {
  context.watch(context.actorOf(props = child, name = name))

  override def receive: Actor.Receive = {
    case msg: PersistImages => context.children.foreach(child => child.tell(msg, sender))
    case msg: Terminated =>
      log.debug("Parent: Terminated recieved")
      probe.tell("controller terminated",sender)
    case msg =>
      log.debug(s"Msg forwarded to probe $msg")
      probe.tell(msg, sender)
  }
}

断言逻辑可以正常工作:

p.expectMsg(4 seconds,"controller terminated")

不确定发生了什么,因为 Terminated msg 似乎无法简单地转发,即使它显然已收到。

【讨论】:

    猜你喜欢
    • 2014-07-11
    • 1970-01-01
    • 1970-01-01
    • 2017-01-24
    • 2018-12-15
    • 2021-05-06
    • 2018-04-20
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多