我是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 _ =>
???
}
}
我知道我们应该更喜欢tell
比ask
。我整理了这个示例,试图消除一些困惑:
Q1-Q2)我知道sender
实际上是context.sender()
。作为代码段中的注释,将捕获哪个sender
?
我的困惑是,sender
是正确的,导致相应的ask
操作吗?还是稍后会出现的sender
条消息?
3)演员在等待ask
的回复时是否能够继续处理新消息?
从实验来看,似乎是这样。有人可以确认吗?
非常感谢!
答案 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}!"))
}