How to create actors pools in akka

Akka is widely used as a backend system for managing high volume transactions, as, it provides a light weight , non blocking , concurrent programming framework that consumes less resources and can scale up or down.

One of the most important factors responsible for the effectiveness of akka actors, is its ability to scale up and down based on the load. The actor pool concept is the most important factor in ensuring elastic scalability.

Actor Pool – Concept

Actor pool refers to a group of instances of actors available for serving requests of a particular kind. For example , consider a retail scenario in which we need to process all order creation events. In this case, “order created” messages can be processed by a pool actors dedicated for processing only order creation messages.

What happens if you use only a single actor ? In this case, each message comes to the inbox of the actor and the actor processes messages one by one , resulting in sequential processing of messages.

Actor pool relies on a group of actors that processes specific message types , so that the messages can be processed in parallel.

Fault tolerance and parallel processing are the goals of the actor pool. To use a pool , we need to use a router.

Message Routing

Router in akka is similar in concept to a network router. The router is responsible to send the message to the destination actor(s) based on the rules. The destination actors are called routees,

Usually, we don’t directly create a Router and use the ones available out of the box with akka. The configuration for the router is specified usually using props file and sometimes using code.

This configuration will have reference to the targeted actors ( the actor we want to receive the messages) and they get added as child to the route actor. Let us look at an example to understand this clearly.

akka.actor.deployment {
  /helloworld {
    router = round-robin-pool
    nr-of-instances = 10
  }
}

Above configuration is a declarative way of defining an actor pool. In this instance it uses a round robin pool. The /helloworld refers to the worker actor to which messages will be delivered by the router. The name helloworld is the name given to the actor. See below

val actor: ActorRef =
  context.actorOf(FromConfig.props(Props[HelloWorldActor]), "helloworld")

With this we have created a actor pool. Any message coming to the helloworld actor will get routed to a pool of helloworld actors containing 10 instances. If 10 messages comes in at the same time, all 10 instances will get a message each and we get 10 parallel actors processing the messages.

Types Routers in Akka

  • RoundRobinRouter
  • RandomRoutingLogic
  • SmallestMailboxRoutingLogic
  • BroadcastRoutingLogic
  • ConsistentHashingRoutingLogic
  • ScatterGatherFirstCompletedRoutingLogic
  • TailChoppingRoutingLogic

For the first 3, what each of those does is evident from their names.

RoundRobinRouter

The messages will be delivered to the actors in an orderly manner using round robin algorithm

akka.actor.deployment {
    /actorname {
        router = round-robin-pool
    }
}

RandomRoutingLogic

akka.actor.deployment {
    /actorname {
        router = random-pool
    }
}

SmallestMailboxRoutingLogic

In this router, the actor with the smallest mail box is chosen for message delivery. If an actor mail box is empty, that actor is chosen, if not the next one with less messages is selected for message delivery.

akka.actor.deployment {
    /actorname {
        router = smallest-mailbox-pool
    }
}

I haven’t included examples for the rest because they not widely used. But there will be scenarios they are of great use.

Dynamically Resizable Pool

The actor pools configured above are static pool, that is they have fixed number of actors created upfront and that count doesn’t increase or reduce in relation to the load. If the application required, elasticity in terms of actor pool scaling, then dynamically resizable pool can help.

There two types of dynamic resizers available in akka

  • Default Resizer
  • OptimalSizeExploringResizer
akka.actor.deployment {
    /actorname {
        router = round-robin-pool
        resizer {
            enabled = on
            lower-bound = 5
            upper-bound = 20
        }
    }
}

In the above configuration the default resizer is specified. This resizer will always maintain a lower bound of 5 actors in the pool and if there is increase in load, it can grow the actor count to a maximum of 20. How it determines when to increase or decrease the actor count is based on pressure ( how much percentage of actors / routees are busy or free)

OptimalSizeExploringResizer provides more options and is more fine tuned than the default resizer. You can explore more on this here at doc.akka.io

More details of the actors pools and they internal workings are explained here

Leave a Comment