Акка Pattern - Актер дерево, ответ на исходный источник
Это вопрос дизайна;
Скажем, у меня есть дерево актеров, которые выполняют кучу обработки. Обработка запускается клиентом/партнером по подключению (т.е. Дерево является сервером). В конце концов, клиент-клиент хочет получить ответ. То есть У меня есть актерская система, которая выглядит так.
ActorA <---reqData--- Client_Actor
| msgA /|\
\|/ |
ActorB |
msgB | \ msgD |
\|/ \/ |
ActorC ActorD---------msgY-->|
|_____________msgX_________|
Ответ, который хочет клиентская система, - это выход из актеров листа (т.е. ActorC
и/или ActorD
). Эти актеры в дереве могут взаимодействовать с внешними системами. Это дерево может быть набором заранее определенных, возможно, маршрутизируемых актеров (т.е. Поэтому Client_actor
имеет только actorref для корня дерева актеров, ActorA
).
Вопрос заключается в том, что является лучшим шаблоном для управления отправкой ответа (msgX
&/или msgY
) от участников финала/листа обратно к клиенту-актеру?
Я могу вспомнить следующие варианты:
- Создайте дерево для каждого клиента подключения и попросите участников отслеживать отправителя, когда они получат
msgX
или msgY
, отправьте его обратно исходному отправителю, чтобы сообщения передавались обратно через дерево. I. Каждый актер будет держать ссылку исходного отправителя.
- Каким-то образом отправьте сообщение
Client_actor
ref в сообщении reqData
и скопируйте его для всех сообщений, используемых в дереве, чтобы субъекты листа могли напрямую ответить на Client_actor
... Это похоже на самый эффективный вариант. Не уверен, как это сделать (я как-то думаю о чертах в классах сообщений, в которых присутствует клиентский агент ref)...
- Как-то найти клиент-клиент на основе уникального идентификатора в сообщениях, переданных через дерево или использовать actorselection (не уверен, насколько хорошо это будет работать с удалением)...
- Что-то лучше...
FYI Я использую Akka 2.2.1.
Ура!
Ответы
Ответ 1
Вы можете использовать метод forward
для пересылки сообщения от исходного отправителя дочернему отправителю на каждом уровне.
в Client_Actor:
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);
Ответ 2
Самый простой способ - отправить сообщения с 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(); }
Ответ 3
В таких ситуациях я написал что-то, называемое ResponseAggregator
. Это экземпляр Actor
, созданный по мере необходимости (а не как постоянный единичный экземпляр), принимающий в качестве аргументов пункт назначения ActorRef
, произвольный key
(чтобы отличить агрегатор, если один получатель получает больше одного агрегатора) предикат завершения, который принимает ответы Seq[Any]
, полученные агентом, и который возвращает true
, если эти ответы представляют собой завершение процесса агрегации и значения тайм-аута. Агрегатор принимает и собирает входящие сообщения, пока предикат не вернет true или истечет время ожидания. Как только агрегация завершена (в том числе из-за таймаута), все отправленные сообщения отправляются в пункт назначения вместе с флагом, указывающим, было ли время ожидания агрегации.
Код слишком велик, чтобы включить сюда и не является открытым исходным кодом.
Чтобы это работало, сообщения, распространяющиеся через систему, должны иметь ActorRef
, указывающие, кому нужно отправить ответное сообщение (я редко создаю актеров, которые отвечают только на sender
).
Я часто определяю поле replyTo
значения сообщения как ActorRef*
, а затем использую класс MulticastActor
, который позволяет оператору !*
отправить нескольким получателям. Это имеет преимущество синтаксической чистоты в построении сообщений (по сравнению с использованием Option [ActorRef] или Seq [ActorRef]) и имеет равные накладные расходы (требующие построения чего-либо для захвата ответа-ответа актера или refs).
В любом случае, с помощью этих вещей вы можете настроить довольно гибкие топологии маршрутизации.