Akka Dispatcher: Everything You Need to Know
Want to learn more about Akka? Here's everything you need to know.
Join the DZone community and get the full member experience.
Join For FreeIn the real world, dispatchers are the communication coordinators responsible for receiving and passing messages. For example, with emergency services like 911, the dispatchers are the people responsible for taking in the call and passing on the messages to the other departments like the medical, fire station, police, etc.
Akka is mostly based on ActorSystem, and as a result, dispatchers are said to be the main engine of an ActorSystem. Hence, the saying: Dispatchers are what makes Akka “tick.” In Akka, they are responsible for selecting an actor, it’s messages, and assigning them to the CPU. So, let’s understand this with an example.
You may also like: [DZone Refcard] Reactive Programming With Akka
Pre-requisite knowledge: Basics of Akka and Actor System.
Fun fact: Did you know that ActorSystem has a heart? Well yes, the ActorSystem is considered to have a heart and it is the “Dispatchers.” We will see how in this blog post.
Consider there are a few actors present in an actor system, as shown in the diagram below.
- Select an actor and pick a message from the mailbox queue of that actor.
- The actor and the message selected are allocated to a thread for its execution.
- The thread to which the allocation is done is now mapped to a processor.
Let’s dive more deeply into it.
Dispatcher Types
In Akka, there are four types of dispatchers:
- Dispatchers (default)
- Pinned Dispatchers
- Balanced Dispatchers
- Calling Thread Dispatchers
Remember: Akka allows us to write our own dispatcher implementation.
1. Dispatchers
First of all, this is the default dispatcher Akka uses when there is no other dispatcher. This is an event-based dispatcher that binds a set of Actors to a thread pool.
Characteristics of default dispatcher are:
- Every actor has its own mailbox.
- The dispatcher can be shared among any number of actors.
- It is designed to be used when you have a non-blocking (async) code in your actor.
- Executers this dispatcher can have is- “fork-join-executer” and “thread-pool-executer”
2. Pinned Dispatchers
This dispatcher provides a single, dedicated thread for each actor. Most importantly, it is useful when actors are performing I/O operations or long-running calculations.
Characteristics of pinned dispatchers are:
- Every actor has its own mailbox.
- A unique actor for each thread implies that this dispatcher is not shareable with any other actor.
- The dispatcher will deallocate the thread attached to the actor after a certain period of inactivity.
- The executor used by this dispatcher is “thread-pool-executor”.
3. Balanced Dispatchers
This is an event-based dispatcher that tries to redistribute work from a busy actor and allocates it to a new one.
Characteristics of a balanced dispatcher are:
- There is only one mailbox present for all the actors.
- Redistribution of tasks can occur only if actors are of the same type.
- Executers this dispatcher can have is- “fork-join-executer” and “thread-pool-executer”.
4. Calling Thread Dispatchers
This dispatcher runs invocations on the current thread only. It does not create any new threads; however, it can be used from different threads concurrently for the same actor. Its major use is in testing.
Characteristics of calling thread dispatchers are:
- Sharing is unlimited in this dispatcher.
- The calling thread is the driver of this dispatcher.
- Each actor has its own mailbox.
Implementation of Dispatchers
Firstly, to implement our own custom dispatcher, we have to create a dispatcher in an application.conf file under the resources folder, as shown below.
Secondly, to understand the configuration of a dispatcher, please read here.
fixed-thread-pool {
type = Dispatcher
executor = "thread-pool-executor"
thread-pool-executor {
fixed-pool-size = 5
}
throughput = 2
}
So, in this example, we have used this custom fixed-thread-pool dispatcher that uses a fixed-pool-size, which we’ve currently set to be five threads only, and interact with it using an Akka actor. So here, we have defined an actor responsible for capturing a request to check for ice-cream inventory.
xxxxxxxxxx
object IceCreamStore {
case class StockRequest(name: String, id: Int)
trait Result
case class IceCreamStockRequest(quantity: Int) extends Result
case class RequestFailure(msg: String) extends Result
}
class IceCreamStockRequestActor extends Actor with ActorLogging{
val randomStock = scala.util.Random
def receive = {
case StockRequest(name, id) =>
log.info(s"CHECKING: ice-cream stock for name = $name, id = $id")
Thread.sleep(5000)
log.info(s"FINISHED: ice-cream stock for name = $name, id = $id")
sender() ! IceCreamStockRequest(randomStock.nextInt(100))
}
}
Now, we define our ActorSystem and wire our custom dispatcher using the system.dispatchers.lookup()
method. We create 10 requests using Akka’s Ask Pattern. Above all, note that we are referencing our fixed-thread-pool dispatcher using the withDispatcher()
method.
xxxxxxxxxx
object IceCreamStockRequestActor extends App{
val system = ActorSystem("IceCreamStoreActorSystem")
implicit val timeout = Timeout(1, TimeUnit.MINUTES)
implicit val executionContext = system.dispatchers.lookup("fixed-thread-pool")
val clientRequests = (1 to 10).map(i => StockRequest("vanilla", i))
val futures = clientRequests.map{ stock =>
val actorRef = system
.actorOf(Props[IceCreamStockRequestActor]
.withDispatcher("fixed-thread-pool"))
(actorRef ? stock).mapTo[IceCreamStockRequest]
}
val results = Await.result(Future.sequence(futures), 1 minute)
results.foreach(println(_))
system.terminate()
}
The output of this will be:
xxxxxxxxxx
[INFO] [09/02/2019 16:01:19.520] [IceCreamStoreActorSystem-fixed-thread-pool-7] [akka://IceCreamStoreActorSystem/user/$b] CHECKING: ice-cream stock for name = vanilla, id = 2
[INFO] [09/02/2019 16:01:19.522] [IceCreamStoreActorSystem-fixed-thread-pool-8] [akka://IceCreamStoreActorSystem/user/$c] CHECKING: ice-cream stock for name = vanilla, id = 3
[INFO] [09/02/2019 16:01:19.520] [IceCreamStoreActorSystem-fixed-thread-pool-6] [akka://IceCreamStoreActorSystem/user/$a] CHECKING: ice-cream stock for name = vanilla, id = 1
[INFO] [09/02/2019 16:01:19.524] [IceCreamStoreActorSystem-fixed-thread-pool-9] [akka://IceCreamStoreActorSystem/user/$d] CHECKING: ice-cream stock for name = vanilla, id = 4
[INFO] [09/02/2019 16:01:19.520] [IceCreamStoreActorSystem-fixed-thread-pool-10] [akka://IceCreamStoreActorSystem/user/$e] CHECKING: ice-cream stock for name = vanilla, id = 5
[INFO] [09/02/2019 16:01:24.523] [IceCreamStoreActorSystem-fixed-thread-pool-7] [akka://IceCreamStoreActorSystem/user/$b] FINISHED: ice-cream stock for name = vanilla, id = 2
[INFO] [09/02/2019 16:01:24.523] [IceCreamStoreActorSystem-fixed-thread-pool-8] [akka://IceCreamStoreActorSystem/user/$c] FINISHED: ice-cream stock for name = vanilla, id = 3
[INFO] [09/02/2019 16:01:24.523] [IceCreamStoreActorSystem-fixed-thread-pool-6] [akka://IceCreamStoreActorSystem/user/$a] FINISHED: ice-cream stock for name = vanilla, id = 1
[INFO] [09/02/2019 16:01:24.526] [IceCreamStoreActorSystem-fixed-thread-pool-7] [akka://IceCreamStoreActorSystem/user/$f] CHECKING: ice-cream stock for name = vanilla, id = 6
[INFO] [09/02/2019 16:01:24.526] [IceCreamStoreActorSystem-fixed-thread-pool-8] [akka://IceCreamStoreActorSystem/user/$g] CHECKING: ice-cream stock for name = vanilla, id = 7
As a result, the above five requests start executing because our fixed-thread-pool configuration only has five threads. Similarly, when they complete, the remaining five requests will be executed, as shown above.
That's it for this blog! I hope you found this blog helpful and that you better understand how dispatcher works.
This article was first published on the Knoldus blog.
Further Reading
[DZone Refcard] Reactive Programming With Akka
Published at DZone with permission of Anjali Sharma. See the original article here.
Opinions expressed by DZone contributors are their own.
Comments