问一个野性的演员选择

时间:2013-09-09 12:26:49

标签: scala akka

是否可以向我们期望匹配多个演员的通配演员选择发送消息,并对所有响应执行某些操作?

我的第一个想法是这样的:

sequence(context.actorSelection("/actors*") ? Message).onSuccess {
    println("The results are " + _)
}

但这不起作用,因为问题不会通过将消息发送给所有演员创建的所有期货返回Iterable,只是在任何演员回复时返回的单个未来。

2 个答案:

答案 0 :(得分:2)

如果您想获得对演员self邮箱的所有回复,您可以使用!方法和明确的sender参数,如下所示:

context.actorSelection("/actors*").!(Message)(self)

Actor selfimplicit,因此编译器会隐式使用它作为sender。您可以发送消息:

context.actorSelection("/actors*") ! Message

如果你想对所有回复做一些特别的事情,你可以创建额外的actor并在sender方法中将其指定为!,但你必须手动停止这个额外的actor。

答案 1 :(得分:2)

这里的困难,正如Senia所指出的那样,你会要求不知名的演员回应,这使你很难知道你何时等待回应。如果你很酷,指定等待的超时,在你得到回复之前总会被击中,那么我想你可以这样做:

//Message to send to the temp actor that handles request/response to the selection
case class AskSelection(path:String, msg:Any, askTimeout:FiniteDuration)

//Actor that handles the request to aggregate responses from a selection
class SelectionAsker extends Actor{
  import context._      
  var responses:List[Any] = List.empty      

  def receive = waitingForRequest

  def waitingForRequest:Receive = {
    case request @ AskSelection(path, msg, askTO) =>                
      system.actorSelection(path) ! msg          
      setReceiveTimeout(askTO)
      become(waitingForResponses(sender, askTO.fromNow))
  }

  def waitingForResponses(originator:ActorRef, deadline:Deadline):Receive = {
    case ReceiveTimeout => 
      originator ! responses
      context.stop(self)          
    case any =>           
      responses = any :: responses
      setReceiveTimeout(deadline.timeLeft)
  }
}

//Factory to create the selection asker
object SelectionAsker{
  def apply(fact:ActorRefFactory) = fact.actorOf(Props[SelectionAsker])
}

这里的一般想法是使用另一个短暂的和临时的演员来处理聚集来自选择的响应。它必须在响应发送方之前等待提供的请求超时的全部数量,因为正如我所提到的,它不知道要等待多少响应。您可以像这样使用它:

val system = ActorSystem("test")
system.actorOf(Props[ActorA], "actor-a")
system.actorOf(Props[ActorB], "actor-b")
implicit val timeout = Timeout(2 seconds)
import system._

val asker = SelectionAsker(system)
(asker ? AskSelection("/user/actor*", "foo", 1 seconds)) onComplete { tr =>
  println(tr)
}

class ActorA extends Actor{
  def receive = {
    case _ => 
      sender ! "a"
  }
}

class ActorB extends Actor{
  def receive = {
    case _ => 
      sender ! "b"   
  }
}

它不漂亮,但如果这是你真正需要做的事情,它可能对你有用。但是你应该确保它首先是你想要做的,并且首先没有更好更容易的选项,因为这需要为你的用例添加一些额外的代码。