Reputation: 29477
I am brand new to Akka and am wondering how I should be handling actors that delegate work out to other (child) actors, but where:
Say I have the following child actors (doesn’t matter what they do):
// Grovy pseudo code
class ActorA extends UntypedActor {
@Override
void onReceive(Object message) {
if(message instanceof RunActorA) {
…
}
}
class ActorB extends UntypedActor {
@Override
void onReceive(Object message) {
if(message instanceof RunActorB) {
…
}
}
class ActorC extends UntypedActor {
@Override
void onReceive(Object message) {
if(message instanceof RunActorC) {
…
}
}
class ActorD extends UntypedActor {
@Override
void onReceive(Object message) {
if(message instanceof RunActorD) {
…
}
}
And say I have the following parent actor that invokes them:
class MasterParent extends UntypedActor {
ActorRef actorA
ActorRef actorB
ActorRef actorC
ActorRef actorD
MasterParent(ActorRef actorA, ActorRef actorA, ActorRef actorA, ActorRef actorA) {
super()
this.actorA = actorA
this.actorB = actorB
this.actorC = actorC
this.actorD = actorD
}
@Override
void onReceive(Object message) {
if(message instanceof ProcessData) {
ProcessData pData = message as ProcessData
Widget widget = pData.widget
RunActorA runA = new RunActorA(widget)
actorA.tell(runA, self)
// Somehow obtain a result from A, perhaps an “ResultOfA” object.
ResultOfA resultA = ??? // get from ActorA
RunActorB runB = new RunActorB(resultA)
RunActorC runC = new RunActorC(resultA)
actorB.tell(runB, self)
actorC.tell(runC, self)
// Somehow wait until both B and C return a result, say, ResultOfB and ResultOfC, respectively.
ResultOfB resultB = ??? // get from ActorB
ResultOfC resultC = ??? // get from ActorC
RunActorD runD = new RunActorD(resultB, resultC)
actorD.tell(runD, self)
}
}
}
As you can see:
ActorA
must be “engaged” (invoked by the parent) first, and we must wait for its response before MasterParent
can go ahead and engage/invoke ActorB
, ActorC
, or ActorD
.ActorB
and ActorC
can be invoked asynchronously/in parallel as one another, but then MasterParent
must wait for response from both before it can go ahead and engage/invoke ActorD
In other words, when a ProcessData
message is received by MasterParent
:
ActorA
and wait for it to return a result; thenActorB
and ActorC
and wait for results from both of them; thenActorD
My main question is: How do I achieve this (Java pseudo-code or examples greatly appreciated!)?
Second, and arguable more importantly, since I’m so new to actors: Is this type of parent-child orchestration “normal” in Akka? Or, do Akka best practices typically dictate that all children run asynchronously and without imposed sequence/order/orchestration?
Upvotes: 0
Views: 752
Reputation: 2173
First of all, yes since the MasterParent is delegating work to the other actors, it makes sense to have them as child actors.
Also your idea of ResultOfA wasn't too far away from.
This is one way you could do it:
ResultOfB resultB = null;
ResultOfC resultC = null;
@Override
void onReceive(Object message) {
if(message instanceof ProcessData) {
ProcessData pData = message as ProcessData
resultB = null;
resultC = null;
Widget widget = pData.widget
RunActorA runA = new RunActorA(widget)
actorA.tell(runA, self)
} else if (message instanceof ResultOfA) {
ResultOfA resultA = message as ResultOfA
actorB.tell(resultA)
actorC.tell(resultA)
} else if (message instanceof ResultOfB) {
resultB = message as ResultOfB
if (resultC != null) {
actorD.tell(composedResultBAndC)
}
} else if (message instanceof ResultOfC) {
resultC = message as ResultOfC
if (resultB != null) {
actorD.tell(composedResultBAndC)
}
} else if (message instanceof ResultOfD) {
//Do stuff with the result
}
}
Where every child actor would do sender().tell(resultX) so that the master receives the result.
ResultB and ResultC are both needed, so I save some state within the actor, which is set to null on every new instance of ProcessData, so it is not possible to send multiple ProcessData to one MasterParent in this case.
If you want to send multiple ProcessData messages to one MasterParent without waiting, you would need to keep it free of state and therefore could employ the Ask Pattern on actorB and actorC and resolve the returning futures together. So something like:
Future futureA = actorB.ask(resultA)
Future futureB = actorC.ask(resultA)
And then compose them and register a callback where you send a request to actorD.
This code pseudo codish, so not runnable.
Upvotes: 1