为什么我的发件人()DeadLetters来自预定的消息?

时间:2014-07-25 14:44:12

标签: scala akka

我正在尝试安排在一段时间后发送的消息 - 简单的重试。

override def receive = {
  case Worked => ???
  case DidNotWork => 
    val target = sender() // Avoid closing over sender().
    import context._
    context.system.scheduler.scheduleOnce(500.milliseconds, target, TryAgain)
}

这可以按预期工作,但是,当我收到TryAgain消息并访问sender()以尝试将此ActorRef发送到此对象时,我得到DeadLetters。为什么会这样?

(注意问题是另一个actor中的sender()调用,它不在闭包中 - 这不是我关闭sender()时的问题):

override def receive = {
  case TryOnce => sender() ! DidNotWork
  case TryAgain => sender() ! Worked // sender() here is DeadLetters!
}

完整示例(回复cmbaxter's comment):

import akka.actor.{Props, ActorSystem, Actor}
import scala.concurrent.duration._

object Main {
  def main(args: Array[String]) {
    val sys = ActorSystem("Test")
    val test = sys.actorOf(Props[Test], "test-actor")
    test ! "badtest"
    test ! "goodtest"
  }
}

class Test extends Actor {
  override def receive = {
    case "badtest" =>
      import context._
      context.system.scheduler.scheduleOnce(10.milliseconds, this.self, "bad")
    case "goodtest" =>
      import context.dispatcher
      context.system.scheduler.scheduleOnce(10.milliseconds, this.self, "good")
    case other => println(s"$sender $other")
  }
}

产生:

Actor[akka://Test/deadLetters] bad
Actor[akka://Test/user/test-actor#621986067] good

2 个答案:

答案 0 :(得分:2)

这是因为import context._电话而发生的。此行导致解析implicit ActorRef函数所需的scheduleOnce()以获取发件人时出现歧义。

ActorRef self存在两次 - 一次在Actor(通常是),一次在函数范围内,来自import {{1} }}。这导致隐式找不到context,而self默认为tell(),这会导致您遇到问题。

如果您注意the example usage of import context._ in the Akka docs,则会在实例级别而非功能级别完成。这意味着DeadLetters会替换self的默认值,而不是遮蔽它,从而消除歧义。

其他选项只能导入Actor以使context.dispatcher调用正常工作,或明确传递。

scheduleOnce()

答案 1 :(得分:0)

因此,在挖掘更多内容时,我同意导入context._是问题所在。 self上有ActorContext,但不是隐含的。将其导入范围时,它会取代每个self附带的隐式Actor。所以现在你在范围内有一个self参考,但它不再含蓄,这就是为什么你会看到一个死信。如果您将来遇到这样的情况,并且您想要完全导入上下文,因为您可以通过将调度移动到actor中的单独方法来轻松解决问题,如下所示:

class Test extends Actor {
  override def receive = {
    case "badtest" =>
      import context._
      scheduleFromMe("bad")
    case "goodtest" =>
      import context.dispatcher
      scheduleFromMe("good")
    case other => println(s"$sender $other")
  }

  def scheduleFromMe(s:String)(implicit ec:ExecutionContext) = context.system.scheduler.scheduleOnce(10.milliseconds, this.self, s)
}

从这个新方法的隐式参数中删除ActorRef允许对调度程序的调用始终拾取作为此actor的隐式self ActorRef