问题
I'm creating a proof of concept with Akka.NET for a production project but I'm facing a query concept understanding problem.
Situation is a follows:
CoordinatorActor has a list of thousands of Hotel-Actors
.
I would like to query all Hotel-Actors
for all hotels with a room available on a specific date.
Of course I could foreach through them and sent a .Ask<>
request for the specific date. Holding a reference of all the tasks and do a Task.WhenAll(requests)
. But that feels a little unnatural.
Alternatively I could send a broadcast message with a request for the specific date to all hotels at once (ActorSelection or router), but then I don't know when they all responded back with a Tell
message.
Does anyone has a suggestion how to solve this?
回答1:
Yes, your feelings are right here. Using Ask for communication between actors is considered highly inefficient - mostly because of each ask needs to allocate separate message listener.
First good question would be: do you need to wait for them all to respond back? Maybe responses can be emitted as they come.
In case, when you need to collect all of the data before replying, you need to have some way of counting all messages in order to guarantee if some of them are still pending - in that case using ActorSelection
is not feasible. You'll need counter or list of identifiers that could be associated with each message - while they can be even ordinary numbers, usually IActorRef
s are easier to work with.
Below you can see simplified example of Aggregator
actor that can be created for this particular task - it will automatically return all replies, it received, and stop itself, once there are no more messages to wait for or a timeout has occurred.
class Aggregator<T> : ReceiveActor
{
private IActorRef originalSender;
private ISet<IActorRef> refs;
public Aggregator(ISet<IActorRef> refs)
{
this.refs = refs;
// this operation will finish after 30 sec of inactivity
// (when no new message arrived)
Context.SetReceiveTimeout(TimeSpan.FromSeconds(30));
ReceiveAny(x =>
{
originalSender = Sender;
foreach (var aref in refs) aref.Tell(x);
Become(Aggregating);
});
}
private void Aggregating()
{
var replies = new List<T>();
// when timeout occurred, we reply with what we've got so far
Receive<ReceiveTimeout>(_ => ReplyAndStop(replies));
Receive<T>(x =>
{
if (refs.Remove(Sender)) replies.Add(x);
if (refs.Count == 0) ReplyAndStop(replies);
});
}
private void ReplyAndStop(List<T> replies)
{
originalSender.Tell(new AggregatedReply<T>(replies));
Context.Stop(Self);
}
}
来源:https://stackoverflow.com/questions/35105339/akka-net-query-actors-efficiently