Message Routing in Akka
Here's an introductory article on message routing in Akka with Scala.
Join the DZone community and get the full member experience.
Join For FreeIn many modern software products, there is a need to accept heavy traffic load, perform multitasking, and run procedures in parallel. Systems have multiple execution cores for executing multiple tasks at the same time rather than sequentially with multithreading engineering.
This makes the system faster but it also causes issues, like shared memory problems. Solutions like locking can protect shared memory values and are fully supported by JVM languages. The drawback is that there is a more complicated code implementation. On the other hand, Akka Actor model is an alternative. The Actor Model provides a higher level of abstraction for writing concurrent and distributed systems.
It also alleviates the developer from having to deal with explicit locking and thread management, making it easier to write concurrent and parallel systems. Actors are objects that encapsulate state and behavior and communicate exclusively by exchanging messages, which are placed into the recipient’s mailbox. Many companies and open-source projects use Akka to develop concurrent systems; Paypal's transaction handling is a very interesting case study.
This is an article about routing in Akka — it would be nice to have the minimum familiarity with Actor model and Scala to get the most out of it.
How to Implement Routing in Akka
In some cases, we may need a specific type of message routing mechanism for specific requirements like:
- Send messages to the less busy actor among other actors of the same type, that is, to the actor with the lowest number of messages.
- Send messages to actors with a round-robin algorithm, that is, send messages one by one to all actors in a loop.
- Send a single message to all the actors in a specific group to redistribute the work among actors automatically with the help of some mechanism.
- Routing allows messages to be routed to one or more actors known as routees, by sending the messages to a router that will know how to route the messages to the routees.
Types of Routing Strategy
In Akka, there are many routing strategies, such as :
-
RoundRobin
: Routes in a round-robin fashion to its routees. -
Random
: This router type selects one of its routees randomly for each message. -
SmallestMailBox
: A Router that tries to send to the non-suspended child routee with fewest messages in mailbox. -
Broadcast
: A broadcast router forwards the message it receives to all its routees. -
ScatterGatherFirstCompleted
: TheScatterGatherFirstCompletedRouter
will send the message on to all its routees. It then waits for the first reply it gets back. This result will be sent back to the original sender. Other replies are discarded. -
TailChopping
: TheTailChoppingRouter
will first send the message to one, randomly picked, routee and then after a small delay to a second routee and so on.
It waits for the first reply it gets back and forwards it back to the original sender. Other replies are discarded.
How Routing Is Designed Within Akka
Routers look like normal actors, but in reality, they are implemented in a different way. Routers are designed to be extremely efficient at receiving messages and passing them quickly on to routees. A normal actor can be used for routing messages, but an actor's single-threaded processing can become a bottleneck. Routers can achieve much higher throughput with optimization to the usual message-processing pipeline that allows concurrent routing.
This is achieved by embedding routers' routing logic directly in their ActorRef
rather than in the router actor. Messages sent to a router's ActorRef
can be immediately routed to the routee, bypassing the single-threaded router actor entirely. The cost to this is, of course, that the internals of routing code are more complicated than if routers were implemented with normal actors.
Below I present a simple routing implementation with RoundRobin
and Broadcast
.
Setup
At first, we need to create an sbt Scala project in our IDE — I prefer IntelliJ — with Akka dependencies.
name := "akka-routing"
version := "0.1"
scalaVersion := "2.12.7"
val akkaVersion = "2.5.13"
libraryDependencies ++= Seq(
"com.typesafe.akka" %% "akka-actor" % akkaVersion,
"com.typesafe.akka" %% "akka-testkit" % akkaVersion,
"org.scalatest" %% "scalatest" % "3.0.5"
)
Round Robin Router
Let's begin by creating an example of a Round Robin Router. As we said before, in this case, the router will send messages one by one to all actors in a loop, as described in the following schema.
At first, we will create an Actor let's call it RoundRobinActor
.
package actors
import akka.actor.{Actor, ActorLogging}
class RoundRobinActor extends Actor with ActorLogging {
override def receive = {
case msg: String => log.info(s" I am ${self.path.name}")
case _ => log.info("Unknown message ")
}
}
Then, we will test it by creating a RounRobinPool
of two actors and route four "hello" messages.
import actors.RoundRobinActor
import akka.actor.{ActorSystem, Props}
import akka.routing.RoundRobinPool
object RoundRobinPoolApp extends App {
val actorSystem = ActorSystem("Akka-RoundRobin-Example")
val router = actorSystem.actorOf(RoundRobinPool(2).props
(Props[RoundRobinActor]))
for (i <- 1 to 4) {
router ! s"Hello $i"
}
}
As we can see in the console, the message routed all routees with the Round Robin algorithm by creating a loop.
[akka://Akka-RoundRobin-Example/user/$a/$a] I am $a
[akka://Akka-RoundRobin-Example/user/$a/$b] I am $b
[akka://Akka-RoundRobin-Example/user/$a/$a] I am $a
[akka://Akka-RoundRobin-Example/user/$a/$b] I am $b
Broadcast Router
Now, let's create an example for Broadcast Router. As we said, the Broadcast Router will forward the message to all its routees, as we can see in the following schema.
Again, we will create an Actor
; let's call it BroadcastActor
.
package actors
import akka.actor.{Actor, ActorLogging}
class BroadcastActor extends Actor with ActorLogging {
override def receive = {
case msg: String => log.info(s" $msg, I am ${self.path.name}")
case _ => log.info("Unknown message ")
}
}
Then, we will test it by creating a BroadcastPool
of three actors and route the message "hello."
import actors.BroadcastActor
import akka.actor.{ActorSystem, Props}
import akka.routing.BroadcastPool
object Broadcastpool extends App {
val actorSystem = ActorSystem("Akka-Example-Broadcast")
val router =
actorSystem.actorOf(BroadcastPool(3).props
(Props[BroadcastActor]))
router ! "hello"
}
As we can see in the console, the message broadcasted successfully to all routees.
[akka://Akka-Example-Broadcast/user/$a/$c] hello, I am $c
[akka://Akka-Example-Broadcast/user/$a/$a] hello, I am $a
[akka://Akka-Example-Broadcast/user/$a/$b] hello, I am $b
This article was an introduction about message routing in Akka with Scala.
Happy routing!
Opinions expressed by DZone contributors are their own.
Comments