这是一个设计问题;
假设我有一组演员,他们会进行一系列处理。该处理由客户端/连接参与者启动(即树是服务器)。最终客户端演员想要一个响应。即我有一个看起来像这样的演员系统。
ActorA <---reqData--- Client_Actor
| msgA /|\
\|/ |
ActorB |
msgB | \ msgD |
\|/ \/ |
ActorC ActorD---------msgY-->|
|_____________msgX_________|
客户端系统需要的响应是叶子参与者的输出(即ActorC
和/或ActorD
)。树中的这些参与者可能正在与外部系统进行交互。这个树可能是一组预定义的可能路由的actor(即Client_actor
只有一个actorref到actor树的根ActorA
)。
问题是管理从最终/叶子演员传回给客户演员的响应(msgX
&amp; /或msgY
)的最佳模式是什么?
我可以想到以下选项;
msgX
或msgY
时,将其发送回原始发件人参考,以便邮件通过通过树回来。即每个演员都会保留原始发件人的引用。 Client_Actor
消息中发送reqData
ref,并为树中使用的所有消息复制此消息,以便叶子演员可以直接回复Client_actor
...这似乎像最高效的选择。不知道如何做到这一点(我在某种方式上考虑了持有客户端参与者的消息案例类的特征)... 仅供参考我正在使用Akka 2.2.1。
干杯!
答案 0 :(得分:21)
您可以使用forward
方法将邮件从原始发件人转发到每个级别的子发件人。
:
actorA ! "hello"
在ActorA中:
def receive = {
case msg =>
???
actorB forward msg
}
在ActorB中:
def receive = {
case msg =>
???
actorC forward msg
}
在ActorC中:
def receive = {
case msg =>
???
sender ! "reply" // sender is Client_Actor!
}
在这种情况下,邮件的“发件人”字段永远不会更改,因此ActorC将回复原始的Client_Actor!
您可以使用允许您指定发件人的tell
方法变体进一步扩展此功能:
destinationActor.tell("my message", someSenderActor);
答案 1 :(得分:1)
最简单的方法是将带有ref的消息发送到源Client_Actor
Client
sendMsg(Client to, Client resultTo)
Client_Actor
req_data(Client to){
sendMsg(to, this);
}
这是一个不错的选择,如果你不知道,哪个客户有原始海报的结果,哪个没有。
如果您知道这一点并且Client_Actor只有一个(就像我们有一个树而且这些只有LEAFS将始终响应并且只有Client_Actor),您可以这样做:
Client
register_actor(Client actor){this.actor = actor;}
call_actor(){ this.actor.sendMsg(); }
答案 2 :(得分:1)
对于这样的情况,我写了一个名为ResponseAggregator
的东西。根据需要实例化Actor
(而不是作为持久性单个实例)将目标ActorRef
作为参数,任意key
(如果单个目标被馈送,则区分聚合器)多个聚合器),一个完成谓词,到目前为止,聚合器接收到Seq[Any]
个持有响应,如果这些响应表示聚合过程完成和超时值,则返回true
。聚合器接受并收集传入消息,直到谓词返回true或超时到期。一旦聚合完成(包括由于超时),所有已接收的消息将被发送到目的地以及指示聚合是否超时的标志。
代码有点太大,不能包含在这里,也不是开源的。
为了使其正常工作,通过系统传播的消息必须带有ActorRef
s,指示响应消息将发送给谁(我很少设计只回复sender
的演员)。
我经常将邮件值的replyTo
字段定义为ActorRef*
,然后使用我的MulticastActor
类,这样!*
“发送给多个收件人”运算符。这具有消息构造中语法清洁的优点(通过与使用Option [ActorRef]或Seq [ActorRef]进行比较)并具有相同的开销(需要构造一些东西来捕获回复的actor ref或refs)。
无论如何,通过这些功能,您可以设置非常灵活的路由拓扑。