Actor消息处理程序中捕获了哪个发件人?

时间:2019-11-19 10:54:06

标签: scala akka

我是Akka演员的新手,我做了如下实验以了解演员的工作方式。

class RequestActor extends Actor with ActorLogging {
  import RequestActor._
  implicit val timeout = Timeout(15.seconds)
  implicit val ec: ExecutionContext = context.dispatcher

  override def receive: Receive = {
    case Request(name) =>
      // self ? TempRequest(TempRequest(name), xxx) pipeTo sender // Q1: if we reference `sender` here, which `sender` will be captured?
      self ? TempRequest(name, sender) onComplete {
        // val whichSender = sender  // Q2: if we reference `sender` here, which `sender` will be captured?
        case Success(TempResponse(msg, origin)) => origin ! HelloReply(msg)
        case Failure(ex) => ???
      }

    case TempRequest(name, origin) =>
      sender ! TempReply(s"hello, ${name}!", origin)

    case _ =>
      ???
  }
}

我知道我们应该更喜欢tellask。我整理了这个示例,试图消除一些困惑:

Q1-Q2)我知道sender实际上是context.sender()。作为代码段中的注释,将捕获哪个sender
我的困惑是,sender是正确的,导致相应的ask操作吗?还是稍后会出现的sender条消息?

3)演员在等待ask的回复时是否能够继续处理新消息?
从实验来看,似乎是这样。有人可以确认吗?

非常感谢!

1 个答案:

答案 0 :(得分:2)

  

//自我? TempRequest(TempRequest(name),xxx)pipe发送给发送方// Q1:如果我们在此处引用sender,将捕获哪个sender

消息Request的发送者。这在您的代码段中不可见。

来自评论

  

自我? TempRequest(TempRequest(name),xxx)管道发送方。由于这是一个询问,将等待,在接收到响应之前,参与者可能会收到另一条消息,从而导致发送方发生更改。因此,pipeTo发送者是否有可能导致响应发送给错误的actor?

pipeTo模式将为?ask模式返回的将来注册一个回调。回调将在回调注册时引用sender()返回的发件人,这在处理Request消息时以阻塞的方式完成。因此,sender()Request消息的发送者。

pipeTo排除了参与者上下文泄漏的任何可能性,这是应该使用它的原因之一。

  

// val whichSender = sender //问题2:如果我们在此处引用sender,将捕获哪个sender

这实际上会导致错误或角色上下文在角色外部泄漏,因此不能这样做。可以使用本地引用来完成,如下面的代码

    case Request(name) =>
      val requestSender = context.sender()
      self ? TempRequest(name) onComplete {
        case Success(TempResponse(msg)) => requestSender ! HelloReply(msg)
      }

来自docs

  

在使用将来的回调(例如onComplete)或映射(例如thenRun或thenApply在actor内部)时,您需要小心避免关闭包含的actor的引用,即,不要在内部的actor上调用方法或访问可变状态。打回来。这将破坏参与者的封装,并且可能会引入同步错误和竞争条件,因为回调将同时安排到封闭的参与者。不幸的是,还没有一种在编译时检测到这些非法访问的方法。

最后

  

3)演员在等待提问时可以继续处理新消息吗?

是的。没有“等待”过程。它只是注册在actor外部执行的回调。因此,可能存在参与者上下文泄漏的问题。

另外,

您可以使用forward保留原始邮件发件人。而且您的代码可以简化为

    override def receive: Receive = {
      case Request(name) =>
        self.forward(TempRequest(name))
      case TempRequest(name) =>
        sender() ! HelloReply(s"hello, ${name}!"))
    }