I'm a newbie with Akka Actors, and I am learning about the Ask pattern. I am looking at the following example from alvin alexander:
class TestActor extends Actor {
def receive = {
case AskNameMessage => // respond to the "ask" request
sender ! "Fred"
case _ => println("that was unexpected")
}
}
...
val myActor = system.actorOf(Props[TestActor], name = "myActor")
// (1) this is one way to "ask" another actor
implicit val timeout = Timeout(5 seconds)
val future = myActor ? AskNameMessage
val result = Await.result(future, timeout.duration).asInstanceOf[String]
println(result)
(Yes, I know that Await.result
isn't generally the best practice, but this is just a simple example.)
So from what I can tell, the only thing you need to do to implement the "askee" actor to service an Ask operation is to send a message back to the "asker" via the Tell operator, and that will be turned into a future on the "asker" side as a response to the Ask. Seems simple enough.
My question is this:
When the response comes back, how does Akka know that this particular message is the response to a certain Ask message?
In the example above, the "Fred" message doesn't contain any specific routing information that specifies that it's the response to a particular Ask operation. Does it just assume that the next message that the asker receives from that askee is the answer to the Ask? If that's the case, then what if one actor sends multiple Ask operations to the same askee? Wouldn't the responses likely get jumbled, causing random responses to be mapped to the wrong Asks?
Or, what if the asker is also receiving other types of messages from the same askee actor that are unrelated to these Ask messages? Couldn't the Asks receive response messages of the wrong type?
Just to be clear, I'm asking about Akka Classic, not Typed.
For every Ask
message sent to an actor, akka creates a proxy ActorRef
whose sole responsibility is to process one single message. This temp "actor" is initialized with a promise, which it needs to complete on message processing.
The source code of it is found here
but the main details are
private[akka] final class PromiseActorRef private (
val provider: ActorRefProvider,
val result: Promise[Any],
....
val alreadyCompleted = !result.tryComplete(promiseResult)
Now, it should be clear that Ask pattern is backed by independent unique actor asker
for every message sent to the receiver askee
.
The askee
does know actor reference of the sender, or asker
, of every message received via method context.sender()
. Thus, it just needs to use this ActorRef
to send a response back to the asker
.
Finally, this all avoids any race conditions given that an actor only processes a message at a time. Thus it excludes any possibility of retrieving a "wrong" asker
via method context.sender()
.