Question

This is a design question;

Say I have a tree of actors which do a bunch of processing. The processing is kicked off by a client/connection actor (i.e. the tree is the server). Eventually the client actor wants a response. I.e. I have an actor system that looks like this.

    ActorA  <---reqData--- Client_Actor
       | msgA                    /|\                      
      \|/                         |                 
    ActorB                        |                  
  msgB |  \ msgD                  | 
      \|/  \/                     | 
    ActorC  ActorD---------msgY-->|
       |_____________msgX_________|

The response that the client system wants is the output from the leaf actors (i.e. ActorC and/or ActorD). These actors in the tree may be interacting with external systems. This tree may be a set of pre-defined possibly routed actors (i.e. so Client_actor just has a actorref to the root of the actor tree, ActorA).

The question is what is the best pattern to manage sending the response (msgX &/or msgY) from the final/leaf actors back to the client actor?

I can think of the following options;

  • Create a tree for each connection client and get the actors to keep track of the sender, when they get a msgX or msgY, send it back to the original sender ref so the messages are passed back up through the tree. I.e each actor will keep a ref of the original sender.
  • Somehow send down the Client_Actor ref in the reqData message and replicate this for all messages used in the tree so the leaf actors can reply directly to the Client_actor... This seems like the most performant option. Not sure how to do this (I'm thinking a trait somehow on the message case classes that holds the client actor ref)...
  • Somehow lookup the client actor based on a unique id in the messages passed through the tree or use the actorselection (not sure how well this would work with remoting)...
  • Something better...

FYI I'm using Akka 2.2.1.

Cheers!

Was it helpful?

Solution

You could use the forward method to forward the message from the original sender to the child sender at each level.

in Client_Actor:

actorA ! "hello"

in ActorA:

def receive = {
  case msg =>
    ???
    actorB forward msg
}

in ActorB:

def receive = {
  case msg =>
    ???
    actorC forward msg
}

in ActorC:

def receive = {
  case msg =>
    ???
    sender ! "reply" // sender is Client_Actor!
}

In this case, the 'sender' field of the message will never change, so ActorC will reply to the original Client_Actor!

You can extend this further by using the tell method variant that lets you specify the sender:

destinationActor.tell("my message", someSenderActor);

OTHER TIPS

The simpliest way is to sending messages with the ref to the source Client_Actor

Client
 sendMsg(Client to, Client resultTo)

Client_Actor
 req_data(Client to){
   sendMsg(to, this);
 }

This is good option, if you dont know, which Client has the result for the original poster and which is not.

If you know this and the Client_Actor is only one (like we have a tree and these and only LEAFS will always response to and only Client_Actor), you can do something like this :

Client
  register_actor(Client actor){this.actor = actor;}
  call_actor(){ this.actor.sendMsg(); }

For situations like this, I wrote something called a ResponseAggregator. It is an Actor instantiated as needed (rather than as a persistent single instance) taking as arguments a destination ActorRef, an arbitrary key (to distinguish the aggregator if a single destination gets fed by more than one aggregator), a completion predicate that takes a Seq[Any] holding responses received by the aggregator so far and which returns true if those responses represent completion of the aggregation process and a timeout value. The aggregator accepts and collects incoming messages until the predicate returns true or the timeout expires. Once aggregation is complete (including due to timeout) all the messages that have been received are sent to the destination along with a flag indicating whether or not aggregation timed out.

The code is a bit too big to include here and is not open source.

For this to work, the messages propagating through the system must bear ActorRefs indicating to whom a response message is to be sent (I rarely design actors that reply only to sender).

I often define the replyTo field of a message value as ActorRef* and then use my MulticastActor class, which enables the !* "send to multiple recipients" operator. This has the advantage of syntactic cleanliness in the message construction (by comparison to using Option[ActorRef] or Seq[ActorRef]) and has equal overhead (requiring the construction of something to capture the reply-to actor ref or refs).

Anyway, with these things, you can set up pretty flexible routing topologies.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top