Самый простой способ - использовать шаблон запроса:
ReceiveAsync<Message>(async msg => {
var t1 = actor1.Ask<Response>(new Request(), cancellationToken);
var t2 = actor2.Ask<Response>(new Request(), cancellationToken);
await Task.WhenAll(t1, t2);
DoSomething(t1.Result, t2.Result);
});
Этот подход будет работать, но у него есть несколько недостатков:
- Он заблокирует актера от обработкилюбые другие сообщения, пока весь метод не завершит выполнение (включая завершение как
t1
, так и t2
). - Используется
Ask
, который имеет свое собственное влияние на производительность - невелик по сравнению, например, с.Ввод / вывод в базу данных, но во многих случаях это может быть слишком много (если вы беспокоитесь о производительности).
Способ решения пункта 1 - перейти с await
:
Receive<Message>(msg => {
var t1 = actor1.Ask<Response>(new Request(), cancellationToken);
var t2 = actor2.Ask<Response>(new Request(), cancellationToken);
Task.WhenAll(t1, t2).ContinueWith(t => {
var t1 = t.Result[0];
var t2 = t.Result[1];
return new CombinedResponse(t1.Result, t2.Result);
}).PipeTo(Self, sender: Sender);
});
Receive<CombinedResponse>(resp => DoSomething(resp.Result1, resp.Result2));
Это не потребует от актера ожидания завершения обеих задач, что делает его свободным для обработки других сообщений в то же время.Однако при этом все еще используются задачи и Ask.
Наконец, возможно использовать только основные примитивы akka и просто объединить частичные ответы самостоятельно.Это часто можно сделать с помощью так называемого Aggregator pattern .Есть несколько способов достижения этого, но в основном все они работают по схожей модели:
- Держите в уме, сколько актеров должно ответить для полного ответа.
- Сохраните частичные ответы вбуфер (список), который будет заполнен входящими ответами от других участников.Когда актер ответил, уменьшите количество актеров, которых нужно ждать.Как только это число достигнет 0, вы собрали полный ответ.
- Сохраните информацию об оригинальном
Sender
актере, который запросил полный ответ, чтобы вы могли отправить его результат (при необходимости), один разполный ответ был собран. - Использование
Context.ReceiveTimeout
предотвращает бесконечное ожидание в случае, если некоторые ответы могут не прийти по какой-либо причине.