Advertisements

Akka Dispatcher- All that you need to know

Reading Time: 4 minutes

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.

In the real world, dispatchers are the communication coordinators responsible for receiving and passing messages. For example, in 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 and it’s messages and assigning them to the CPU. So let’s understand this with an example.

Consider there are a few actors present in an actor system as shown in the diagram below.

The role of the dispatcher is to:

  1. Select an actor and pick a message from the mailbox queue of that actor. 
  2. The actor and the message selected are allocated to a thread for its execution.
  3. The thread to which the allocation is done is now mapped to a processor.

So let’s dive more deeply into it.

Dispatcher types:

In Akka, there are four types of dispatchers:

  1. Dispatchers(default)
  2. Pinned Dispatchers
  3. Balanced Dispatchers
  4. 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 Dispatcher are:

  • Every actor is 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 allocate it to a new one.

Characteristics of 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 in your code:

Firstly, to implement our own custom dispatcher, we have to create a dispatcher in application.conf file under the resources folder as follows:
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 which uses a fixed-pool-size, which we’ve currently set to be 5 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.

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 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.

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:

[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 5 requests starts executing because our fixed-thread-pool configuration only has 5 threads. Similarly, when they complete, the remaining 5 requests will execute as shown above. 

That was it for this blog. I hope this blog helped you in understanding how dispatcher works.

References:

  1. https://doc.akka.io/docs/akka/current/dispatchers.html
  2. https://www.packtpub.com
  3. https://blog.knoldus.com/akka-and-futures/
Advertisements

2 thoughts on “Akka Dispatcher- All that you need to know5 min read

Comments are closed.

Knoldus Pune Careers - Hiring Freshers

Get a head start on your career at Knoldus. Join us!

%d bloggers like this: