1. Introduction
Akka is a very popular toolkit for building asynchronous and concurrent applications using the actor model. Actors communicate with each other using messages and mailboxes. Akka provides two patterns for communication:
- the ask pattern where the caller expects an asynchronous response
- the fire-and-forget pattern where the caller doesn’t expect or wait for any response
In this tutorial, let’s discuss the difference between the methods tell() and forward() in the fire-and-forget pattern.
2. The tell() Method
When we use the tell() method to send a message to another actor, it sets the sender of the message as itself. The operator ! represents the tell() method.
Let’s assume that an actor ActorA sends the message to ActorB using tell(). Then, ActorB sends the exact same message to ActorC using the tell() method. If ActorC checks who sent this message, it shows the sender as ActorB:
Let’s implement a simple example to see this in action:
case object TellMsg
class ActorA(actorB: ActorRef) extends Actor {
override def receive: Receive = {
case msg:Any => actorB ! msg
}
}
class ActorB(actorC: ActorRef) extends Actor {
override def receive: Receive = {
case TellMsg => actorC ! "Tell"
}
}
class ActorC extends Actor {
override def receive: Receive = {
case msg: Any => println(s"Received message: $msg, sender: ${sender.path.name}")
}
}
Now that we’ve created the actors, we can instantiate them and send the message using tell():
val system = ActorSystem("MySystem")
val actorC = system.actorOf(Props(new ActorC()), "ActorC")
val actorB = system.actorOf(Props(new ActorB(actorC)), "ActorB")
val actorA = system.actorOf(Props(new ActorA(actorB)), "ActorA")
actorA ! TellMsg
When we execute this, we can see that the sender name is printed as ActorB:
3. The forward() Method
The forward() method works similarly to the tell() method except that the sender of a forwarded message is kept as the original sender. As a result, the actor receiving the forwarded messages knows who the actual sender of the message is:
In the previous case, if we use forward() instead of tell() in the ActorB, then the sender value isn’t changed.
Let’s add another case statement to ActorB to handle the forwarded message, keeping ActorA and ActorC exactly the same:
class ActorB(actorC: ActorRef) extends Actor {
override def receive: Receive = {
case TellMsg => actorC ! "Tell"
case ForwardMsg => actorC forward "Forward"
}
}
Now, we can send ForwardMsg to ActorA:
actorA ! ForwardMsg
When we execute this, we get the output showing that the sender is ActorA:
We can explicitly implement the forward method in terms of the tell() method. Let’s rewrite the case statement for the forward() method in ActorB:
case ForwardMsg => actorC.tell("Forward", sender)
The above code block explicitly passes the sender value and is equivalent to forward().
4. Conclusion
In this short article, we looked at the difference between the tell() and forward() methods of Akka Actors. We also looked at how we can implement forward() in terms of tell(). In conclusion, we can say that if we want to know the original sender of a message in a chain of actor messages, it’s better to use forward().
As always, the sample code used in this article is available over on GitHub.