In recent times the term of reactive programming is being listened more and more. This one is a programming paradigm oriented to data flows and the propagation of change. Currently, Akka is becoming one of the most popular free and open-source toolkits for constructing concurrent and distributed applications on the JVM.
This post explains which are the Akka foundations, and how the model of concurrency used in this technology works. With all this information we’ll know the benefits of developing applications using Akka, and we’ll get enough knowledge to understand practical examples.
A long time ago in a galaxy far far away …
Java Enterprise Edition (JEE) was introduced as a platform to develop and run distributed multi-tier Java applications. The entire multi-tier architecture relied extensively on modular software components deployed in containers, called application servers.
Although the container-based model allows you to distribute applications across nodes using multi-threaded programming, this model has three major flaws:
- Shared objects: protected by synchronized block or method but blocking.
- Deadlock: first thread tries to access synchronized block of second thread, while second thread tries to access synchronized block from first thread, resulting in deadlock.
- Scalability: managing threads on multiple JVMs.
Over the last few years there has been a growing need for the use of distributed systems which provide high concurrency, and hence being able to execute distributed applications in an efficient way is imperative.
It is hard writing safe and correct multithreaded code with traditional JVM programming techniques, like threads and locks. Therefore there is a need for a different paradigm to solve the problem.
A new hope
There are other different approaches for modeling concurrent systems, one of them is the Actor Model. This model was first proposed in 1973, but at this time the development of distributed networks was in its infancy. Erlang was the first programming language to adopt Actor Model in 1986.
In 2006 an actor implementation was released as part of Scala. By 2008 Scala was attracting attention for use in complex server applications, but concurrency was still typically achieved by creating threads that shared memory and synchronized when necessary using locks.
Then Erlang programming language inspired Akka to write highly concurrent, event-driven applications with a different paradigm. Akka is now part of the Lightbend Platform together with the Play framework and the Scala programming language.
The galaxy is counting on us
A reactive system is composed of several components, the more relevant pieces in AKKA are the ActorSytem, the Actors, the Dispatchers, the Mailboxes and the ActorRefs.
Going from top to bottom the first component is the ActorSystem. It is the galaxy where all the events happen, it is the context which creates and initializes all the actors. The actors are objects which encapsulate state and behavior, they communicate exclusively by exchanging messages.
Each actor has a Mailbox, where messages are picked from the queue and processed one at a time.
Every time a message is processed, the actor executes methods for running a determined action. The executions of the methods associated with the actor determine its own state, but in contrast with the JEE platform we avoid using shared objects for saving the state, and hence avoid the use of locks and synchronized access methods.
An actor can respond to the received message by sending immutable messages to other actors, creating a new set of actors, updating their own state, or designating the computational logic to be used when the next message arrives (behavior change).
When we model the solution for a reactive system we can think of the actors like people, or in our case lifeforms in the galaxy. We can assign individual subtasks to those people and we arrange their functions into an organizational structure.
The communication between actors is decoupled and asynchronous, allowing them to execute in different threads. It’s a very good practice to apply the Single Responsibility Principle when we design our actor system. Doing that we’ll ensure every actor performs unique simple tasks executed in different threads, therefore by having invocation and execution in separate threads decoupled with no shared state, allows actors to provide a concurrent and scalable model.
Help me, Obi-Wan Kenobi. You’re my only hope
Messages are passed between actors asynchronously. Any actor can send a message to another actor with no guarantee on the sequence of the message arrival and execution. There are two modes for passing messages asynchronously to an actor:
- Fire and forget: one-way message model, where the producer of the message expects no reply from the consumer. The message is sent asynchronously and the method returns immediately. The command for using this mode is the tell() method in Java and “!” in Scala.
- Send and receive: two-way message model, where the producer of the message expects a reply from the consumer and will wait for that reply. In this mode the consumer returns a future which represents a potential reply. The command for using this mode is the ask() method in Java and “?” in Scala.
The circle is now complete
Let’s see in detail the process of sending messages between actors watching all the components involved and how they work altogether.
- The sender actor initialises the actor creation process invoking the actorOf() command on ActorSystem.
- The ActorSystem creates a reference of an Actor object. At this time the Actor may not be created/initiated.
- The sender actor uses the tell() method or the “!” operator to ask the receiving actor something like: “Hi actor, can you please process this event?”
- ActorRef makes the message flows to the MessageDispatcher.
- MessageDispatcher enqueues the message inside the MessageQueue of the Mailbox.
- MessageDispatcher binds a set of actors to a thread pool.
- MessageDispatcher invokes the mailbox.run() method for starting to process the messages in the Mailbox.
- The message is dequeued from the MessageQueue of the Mailbox.
- The Mailbox invokes the receive() method on the actor to process the messages and execute the methods associated with these messages.
Luke, I am your father
The first thing that every Akka application does is create an ActorSystem, which is itself also an actor, and the entry point for creating or looking up all the other actors. The actor system can create top-level actors. It is a common pattern to create only one top level actor for all actors in the application. This actor in Akka is called the SupervisorActor and its mission is to monitor everything.
The supervisor hierarchy is simply a function of the act of actors creating each other: every actor that creates another is the supervisor of the created child actor. The hierarchy is fixed for the lifetime of a child. Once the child is created by the parent, it will fall under the supervision of that parent as long as it lives. The parent terminating a child actor is the only way for the supervisor to cease its responsibilities.
This hierarchy is used in Akka to identify the location of the actor. Every actor has a name, and this name needs to be unique at each level in the hierarchy. For instance, two sibling actors can’t have the same name. If you don’t provide a name, Akka generates one for you, but it’s a good idea to name all your actors. All actor references can be located directly by an actor path, absolute or relative.
Don’t underestimate the power of the Force
Akka is a toolkit and runtime for building highly concurrent, distributed, and resilient message-driven applications on the JVM. Thanks to the Actor Model paradigm, Akka allows building powerful applications in a very simple way, providing libraries in two different languages Scala and Java.
The main features of Akka are:
- Simple concurrency and distribution: asynchronous and distributed by design. High-level abstractions like Actors, Streams, and Futures.
- Resilient by design: Write systems that self-heal. Remote and local supervisor hierarchies.
- High performance: 50 million msg/sec on a single machine. Small memory footprint; ~2.5 million actors per GB of the heap.
- Elastic and decentralized: adaptive cluster management, load balancing, routing, partitioning, and sharding.
- Extensibility: several Akka extensions to adapt Akka to fit your needs (akka-http, akka-persistence, akka-streams).
The Force is with you, but you are not a Jedi yet
We’ve seen the theory about how the Actor Model works and some of their amazing powerful characteristics for running scalable, reactive applications easily.
By using actors and immutable messages as a communication mechanism we can simplify and improve the concurrency of processes and avoid saving a shared state object, and hence avoid the use of synchronized methods.
At this point, we know how the technology works, but we don’t know how to apply in real life yet, so…
- Akka Essentials. A practical, step-by-step guide to learn and build Akka’s actor-based, distributed, concurrent, and scalable Java applications. Munish K. Gupta. ISBN 978-1-84951-828-4.
- Akka in Action. Raymond Roestenburg, Rob Bakker, and Rob Williams. MEAP Edition. Version 17. ISBN 978-1-61729-101-2.