DZone
Thanks for visiting DZone today,
Edit Profile
  • Manage Email Subscriptions
  • How to Post to DZone
  • Article Submission Guidelines
Sign Out View Profile
  • Post an Article
  • Manage My Drafts
Over 2 million developers have joined DZone.
Log In / Join
Please enter at least three characters to search
Refcards Trend Reports
Events Video Library
Refcards
Trend Reports

Events

View Events Video Library

Zones

Culture and Methodologies Agile Career Development Methodologies Team Management
Data Engineering AI/ML Big Data Data Databases IoT
Software Design and Architecture Cloud Architecture Containers Integration Microservices Performance Security
Coding Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks
Culture and Methodologies
Agile Career Development Methodologies Team Management
Data Engineering
AI/ML Big Data Data Databases IoT
Software Design and Architecture
Cloud Architecture Containers Integration Microservices Performance Security
Coding
Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance
Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks

Modernize your data layer. Learn how to design cloud-native database architectures to meet the evolving demands of AI and GenAI workkloads.

Secure your stack and shape the future! Help dev teams across the globe navigate their software supply chain security challenges.

Releasing software shouldn't be stressful or risky. Learn how to leverage progressive delivery techniques to ensure safer deployments.

Avoid machine learning mistakes and boost model performance! Discover key ML patterns, anti-patterns, data strategies, and more.

  1. DZone
  2. Refcards
  3. Reactive Programming with Akka
refcard cover
Refcard #201

Reactive Programming with Akka

Event-Driven, Scalable, Resilient, and Responsive Java and Scala

Dives into Akka, covering defining and creating actors and messages, actor hierarchies, fault-tolerance and self-healing, and more.

Download Refcard
Free PDF for Easy Reference
refcard cover

Written By

author avatar Johan Andrén
Developer Akka Team, Lightbend
author avatar Ryan Knight
Flex Architect, Gorilla Logic
Table of Contents
► Creating a New Akka Application ► What Is the Actor Model? ► Defining an Actor ► Creating Actors ► Defining Messages ► Tell the Actor (To Do Something) ► The 'self' Reference ► The 'sender' Reference ► Actor Hierarchies ► Fault-Tolerant and Self-Healing ► Further Learning
Section 1

Creating a New Akka Application

Akka is a toolkit and runtime for building concurrent, distributed, and reactive applications and systems on the JVM. The Reactive Manifesto (reactivemanifesto.org) defines reactive in terms of its four guiding principles

1. Message-driven

2. Elastic

3. Resilient

4. Responsive

Message-driven means the application reacts to events by using a message-driven programming model. This allows the application to more effectively share resources by doing work only in response to outside messages.

Elastic means the application is able to react to increasing load by making the architecture highly concurrent and distributed.

When an application is Resilient, it can easily deal with failure and recovery. Instead of the application simply dying, it manages failure through fault isolation so other parts of the application can keep running.

The final property, being Responsive, means the application responds quickly not only in ideal conditions, but also when there are application failures.

Event-driven programming model

Akka was designed to enable developers to easily build reactive systems using a high level of abstraction, without having to deal with low-level concepts like threads, mutexes, and deadlocks. It does so by leveraging theActor Model of concurrency and fault-tolerance. This is a powerful model that allows the behavior and state of the application to be encapsulated and modeled as an actor in a very natural and simple way. The key principle behind an actor is that the application only interacts with it through messages and never talks with it directly. This abstraction allows actor-based applications to seamlessly scale from a single machine to large clusters.

To create a new Akka application, use your favorite IDE and build tool to create a new empty project. Akka is provided as regular jar files available through Maven central, so you can use it by just adding the following dependency to your project (after taking a look at akka.io to see which is the latest stable version). NOTE: 2.5.0 will be available very soon.

Maven:

​x
1
<dependency>
2
  <groupId>com.typesafe.akka</groupId>
3
  <artifactId>akka-actor_2.11</artifactId>
4
  <version>2.5.0-RC1</version>
5
</dependency>
6
​

Gradle:

1
1
compile 'com.typesafe.akka:akka-actor_2.11:2.5.0-RC1'

SBT:

1
1
"com.typesafe.akka" %% "akka-actor" % "2.5.0-RC1"

With the project in place, create a simple Java class called Application with a main method, and inside of that, create an ActorSystem:

15
1
import akka.actor.ActorRef;
2
import akka.actor.ActorSystem;
3
import akka.actor.Props;
4
​
5
public class Application {
6
​
7
  public static void main(String[] args) throws Exception {
8
    final ActorSystem system = ActorSystem.create();
9
​
10
    System.out.println("Press any key to terminate");
11
    System.in.read();
12
    System.out.println("Shutting down actor system...");
13
    system.terminate();
14
  }
15
}

ActorSystem starts and maintains thread pools, and will keep running until you explicitly tell it to terminate, so in this sample we block the main thread until a key is pressed, which triggers termination. If we did not do that, the main method would return and the ActorSystem would keep running.

This Refcard introduces Akka by modeling a simple robot named AkkaBot with Akka’s  Java APIs. There is a full set of corresponding Scala APIs, but those will not be covered. The AkkaBot will be modeled as an actor, which is either moving or still, and has a direction it is moving in.

Section 2

What Is the Actor Model?

The Actor Model was originally invented by Carl Hewitt in 1973 and has seen a recent resurgence in popularity for concurrency. In the Actor Model, an application is broken up into small units of execution called actors. These actors encapsulate the behavior and state of a part of the application. The first part of the definition of actors makes them sound very much like objects in a standard object-oriented design, which is true. However, what distinguishes an actor from a standard object is a third property: communication.

Actors never interact directly with each other and instead communicate via messages. The behavior of an actor is primarily defined by how it handles messages as they are received. Since the state of an actor is isolated from the rest of the system, an actor never has to worry about synchronization issues, such as thread locking, when modifying its state.

Where the Actor Model becomes even more interesting is when you combine actors into a supervisory hierarchy. By organizing the actors into a hierarchy, you can have parent actors that manage child actors and delegate computations to child actors. By leveraging the actor hierarchy, it becomes very easy to make an application fault-tolerant and scalable.

Actors in Akka are extremely lightweight since they are standard objects that only consume a few hundred bytes each. The only constraint in number of actors is the amount of memory they consume. This means that a single application can easily create thousands or even millions of concurrent actors.

An important part of the Actor model is that you never create actors directly, Instead, they are created by the ActorSystem. The returned value is not the actual actor; instead it is a reference to that actor called an ActorRef. There are three advantages to using this level of indirection and never accessing the actor directly:

  1. The actor can be transparently restarted after failure.
  2. The location of the actor is transparent, allowing Akka to manage when and where the actor runs.
  3. The actor is able to maintain mutable state without worrying about concurrent access to this state.

Shared mutable state is a cause of many concurrency problems. Akka actors solve this problem by processing a single message at a time and transparently handling memory visibility when the actor executes on different threads over time. Many threads can hold an ActorRef and send messages to it concurrently, yet the actor will process them in a linear fashion. Accessing or mutating the internal state of an actor is fully thread safe since the thread is protected by the Actor Model.

The strong isolation principles of actors, together with the message-driven model and location transparency, make it easy to solve hard concurrency and scalability problems in an intuitive way. It abstracts away the underlying threading model and actor scheduling so you can focus on building your application and not infrastructure.

An actor is created by defining a class that extends AbstractActor and implements the createReceive method to define how the actor should react to the different messages it receives.

To start building our robot, the AkkaBot, we first need to create an actor that will represent it. It will contain information about whether the robot is moving the direction of movement. It has an onReceive method that defines its behavior for how it should react upon receiving a move message.

Section 3

Defining an Actor

  1. Create the file AkkaBot.java in the source directory and enter the Java code below:
9
1
import akka.actor.AbstractActor;
2
​
3
public class AkkaBot extends AbstractActor {
4
​
5
    public Receive createReceive() {
6
      return receiveBuilder().build();
7
    }
8
}
9
​
Section 4

Creating Actors

Now that we have defined the actor, let's create an instance of it. Since we need Akka to manage the actor’s lifecycle, we don’t create it directly just using the “new” operator, but instead ask the ActorSystem to create and manage the actor for us. What we get back is not an instance of the actor itself, but an ActorRef pointing to our actor instance.

This level of indirection adds a lot of power and flexibility. It enables location transparency, which means that the ActorRef can, while retaining the same semantics, represent an instance of the running actor in-process or on a remote machine (i.e. location doesn't matter). This also means that the runtime can optimize the system by changing an actor's location or the application's topology while it is running. This level of indirection also enables the "let it crash" model of failure management, in which the system can heal itself by crashing and restarting faulty actors.

To create (top level) actors in Akka you use the ActorSystem.actorOf factory method. This method takes a configuration object called Props and an optional name.

The ActorSystem in Akka is also much more than just a factory for creating actors. It manages the entire lifecycle of the actor, maintains the execution contexts (thread pools) in which actors run, a scheduling service, an event stream of what is happening, and more.

Previously, we added the basic code for creating an ActorSystem and the AkkaBot in the Application class. Now we’ll update it to actually create an instance of your actor in the main method:

6
1
public static void main(String[] args) throws Exception {
2
    final ActorSystem system = ActorSystem.create();
3
​
4
    final ActorRef akkaBot = system.actorOf(
5
      Props.create(AkkaBot.class),
6
      "akkaBot");

Creating an actor using the ActorSystem directly creates the actor at the top of the hierarchy. Actors can also be created as children of other actors using an actor’s local ActorContext. The ActorContext contains information about the ActorSystem relevant to each actor, such as who its parent and children are. When an actor uses this context to create another actor, the new actor becomes a child actor. In this way, the actor hierarchy gets built out.

Section 5

Defining Messages

An actor does not have a public API in terms of methods that you can invoke. Instead, its public API is defined through a protocol, the set of messages, that the actor handles. Messages can be of arbitrary type (any subtype of Object). This means that we can send boxed primitive values (such as String, Integer, Boolean, etc.) as messages or plain data structures like arrays, collection types, and value objects. However, since the messages are the actor's public API, you should define messages with good names and rich semantic and domain-specific meaning, even if it's just wrapping your data type. This makes it easier to use, understand, and debug actor-based systems.

This is typically done using public static classes defined together in the beginning or end of the actor class. Then, when someone is looking at the actor code, they can easily see what messages are handled for the actor. This also makes the messages part of the auto-generated Javadoc.

Now we want to define three different messages:

  • Move starts the robot moving
  • Stop stops the robot

Let's define the messages by putting them inside the AkkaBot class. It is very important that the messages we create are immutable, meaning that they cannot be changed after construction, this guarantees that it is safe to share the messages between different threads of the JVM without using any concurrency primitives such as locks.

18
1
// Inside AkkaBot.java code add the following inner classes
2
public enum Direction { FORWARD, BACKWARDS, RIGHT, LEFT }
3
public static class Move {
4
    public final Direction direction;
5
    public Move(Direction direction) {
6
        this.direction = direction;
7
    }
8
}
9
public static class Stop {}
10
public static class GetRobotState {}
11
public static class RobotState {
12
    public final Direction direction;
13
    public final boolean moving;
14
    public RobotState(Direction direction, boolean moving) {
15
        this.direction = direction;
16
        this.moving = moving;
17
    }
18
}
Section 6

Tell the Actor (To Do Something)

All communication with actors is done through asynchronous message passing. This is what makes actors reactive. An actor doesn't do anything unless it's been told to do something, and you tell it to do something by sending the message.

Sending a message asynchronously means that the sender does not stick around waiting for the message to be processed by the recipient actor. Instead, the sender hands the message off by putting it on the recipient's Mailbox and is then free to do something more important than waiting for the recipient to react on the message.

The mailbox is not accessed directly in code, the sender of a message just sends a message to the ActorRef, and the actor simply has the message delivered to it, however it is useful to remember that the messages actually sit around in the mailbox until processed.

Image title

The actor's mailbox is essentially a message queue and has ordering semantics. This guarantees that the ordering of multiple messages sent from the same sender is preserved, while the same messages can be interleaved with the messages sent by another sender. When the actor is not processing messages it does not consume any resources, apart from memory.

You tell an actor to do something by sending it a message with the tell method on the ActorRef. This method puts the message on the actor's mailbox and then returns immediately.

Internally, the actor chooses its behavior depending on the type and properties of incoming messages. This behavior can be any standard logic such as modifying the internal state of an actor, creating or sending messages to other actors, business logic, or even changing the behavior of the actor itself.

To define the behaviors of an actor, you implement createReceive and return an instance of Receive, which is created using the method receiveBuilder(). After defining the types of messages that are handled, the builder is made to construct a Receive by calling build().

First let’s define the behavior for the Move and Stop messages. Make the following changes to AkkaBot.java:

21
1
// Add the following inside AkkaBot
2
private Optional<Direction> direction = Optional.empty();
3
private boolean moving = false;
4
​
5
public Receive createReceive() {
6
    return receiveBuilder()
7
            .match(Move.class, this::onMove)
8
            .match(Stop.class, this::onStop)
9
            .build();
10
}
11
​
12
private void onMove(Move move) {
13
    moving = true;
14
    direction = Optional.of(move.direction);
15
    System.out.println("I am now moving " + direction.get());
16
}
17
​
18
private void onStop(Stop stop) {
19
    moving = false;
20
    System.out.println("I stopped moving");
21
}

We can now test out the actors by sending them some simple commands from the Bot Main app.

Add the following to the main method in the Application class:

9
1
akkaBot.tell(
2
    new AkkaBot.Move(AkkaBot.Direction.FORWARD), 
3
    ActorRef.noSender());
4
akkaBot.tell(
5
    new AkkaBot.Move(AkkaBot.Direction.BACKWARDS),
6
    ActorRef.noSender());
7
akkaBot.tell(
8
    new AkkaBot.Stop(),
9
    ActorRef.noSender());

When you run the application, you should see some basic logging of the application such as:

3
1
I am now moving FORWARD
2
I am now moving BACKWARDS
3
I stopped moving
Section 7

The 'self' Reference

Sometimes the communication pattern is not just one-way, but instead lends itself towards request-reply. One explicit way of doing that is by adding a reference of yourself as part of the message so the receiver can use that reference to send a reply back to you. This is such a common scenario that Akka directly supports it. For every message you send, you have the option of passing along the sender reference (the actor's ActorRef). If you are sending a message from within an actor, you have access to your own ActorRef through the self-reference. You can access the self-reference through the getSelf() method.

4
1
// From within an actor
2
actorRef.tell(
3
    new AkkaBot.Move(AkkaBot.Direction.FORWARD), 
4
    getSelf());

If you are not inside an actor or do not want to pass the sender, use ActorRef.noSender() instead. An example of this case is in the main class where messages were sent to the robot without a sender.

Section 8

The 'sender' Reference

Since each message is paired with its unique sender reference, the "current" sender reference will change with each new message you process. You can access it using the getSender() method. For example, to send a message back to the sender of the message that is being handled, use:

2
1
// From within an actor
2
getSender().tell(new Greeting(greeting), getSelf());

If you need to use a specific sender reference after some asynchronous processing, like after messaging with other actors, you will need to store a reference to the sender in a variable. The reason for this is that the sender might change if other messaging or processing happens in the meantime.

In some cases there might not be a sender, like when a message is sent from outside an actor or the sender was Actor.noSender. In these cases, sending a message to sender would cause it to be sent to dead letters. The dead-letter actor is where all unhandled messages end up. For debugging and auditing purposes, you can watch for messages to the dead-letter actor.

Section 9

Actor Hierarchies

The real power of the Actor Model is in actor hierarchies. An actor hierarchy is when a parent actor creates and supervises child actors. This structure helps avoid cascading failures that can take down an entire system by isolating and supervising child nodes.

Creating child actors is very similar to creating top level actors - the only difference is the context the child actors are created in. The actor context can be thought of as where in the actor hierarchy an actor lives. As an example, let’s create a Bot Master that creates several children. Add the class BotMaster that creates several children in the constructor:

16
1
import akka.actor.AbstractActor;
2
import akka.actor.Props;
3
​
4
public class BotMaster extends AbstractActor {
5
​
6
    public BotMaster() {
7
        for (int index = 0; index < 10; index++) {
8
            getContext().actorOf(Props.create(AkkaBot.class));
9
        }
10
    }
11
​
12
    @Override
13
    public Receive createReceive() {
14
        return emptyBehavior();
15
    }
16
}

We access the local actor context and create new actors in that context. That makes all of the AkkaBot actors we created children of the Bot Master actor. Now that the Bot Master has children, it can interact with the children directly. To do this, let’s add a simple method to make the child bots move.

Add the StartChildBots message:

2
1
// inside the Bot Master class
2
public static class StartChildBots {}

Then modify what messages the Bot Master handles:

15
1
// replace the previous emptyBehavior
2
public Receive createReceive() {
3
    return receiveBuilder()
4
            .match(StartChildBots.class, this::onStartChildBots)
5
            .build();
6
}
7
​
8
private void onStartChildBots(StartChildBots startChildBots) {
9
    final AkkaBot.Move move = 
10
        new AkkaBot.Move(AkkaBot.Direction.FORWARD);
11
    for (ActorRef child : getContext().getChildren()) {
12
        System.out.println("Master started moving " + child);
13
        child.tell(move, getSelf());
14
    }
15
}

What this does is look in the Master Bot’s context for all of its children. It then iterates through the children and sends each of them a message. As the message is immutable, it is perfectly safe to send the same message instance to all the children.

To test this out, let’s modify the Akka Bots so they print out their own path to make it easier to see where trace statements are coming from. Add a prefix of getSelf().path() to the println’s in AkkaBot:

1
1
System.out.println(getSelf().path() + ": I am now moving " + direction.get());

Then modify the Application main method to start the Bot Master instead of the AkkaBot:

5
1
final ActorRef botMaster = system.actorOf(
2
    Props.create(BotMaster.class),
3
    "botMaster");
4
​
5
botMaster.tell(new BotMaster.StartChildBots(), ActorRef.noSender());

The method call to self.path gets the path of the current actor. This path will be something like:

1
1
/user/botMaster/$a

User is a top level system actor that is always the root parent of all user-created actors, botMaster we created by calling ActorSystem.actorOf, and $a is the actual bot actor. Since we didn’t give it an explicit name, Akka gave it a name that is unique among its siblings. From the path we can see this actor is a child of botMaster.

Image title

A child actor can get the ActorRef of its parent through getContext().parent().

This exercise should give you a good sense for the context in where an actor lives in the hierarchy of other actors, since you can see how an actor can get its parents and children.

Section 10

Fault-Tolerant and Self-Healing

The reason actor hierarchies are so powerful is they provide a way to build systems that are fault-tolerant and self-healing. This can be done by isolating the errors to child actors and monitoring those actors for failures.

Failure (exceptions) is a normal part of any application, and can be a result of a wide variety of causes. With actors, the failures can happen during startup, message processing, or other lifecycle events. What is unique about the actor model is that failure doesn’t cause the entire application to crash or pass the responsibility to solve the problem to the calling code; rather the fault is isolated to an individual actor and the failure is passed to its parent, which gets to decide what should happen.

There are two ways actors can deal with failures. The first is through customizing the supervision strategy. Each actor has a default supervisor strategy that defines how it handles failures in its children. The default supervisor strategy for an exception is to restart the actor. This supervisor strategy can be overridden to define a different behavior. Besides restart, other possible actions include escalating the exception up the hierarchy, simply resuming the actor, or stopping the actor.

Resuming and restarting might seem to be the same, however there is one key difference. When an actor is restarted, a new instance of the actor is created, which resets its state. If an actor is resumed, the actor state is maintained. With both strategies the message that caused the fault is lost, but the actor does not lose any of its other messages waiting in the mailbox.

The second way a parent can manage the failures of its children is to watch the children. Then the parent receives an akka.actor.Terminated message when the child is terminated. As an example, let’s assume that if a child robot stops, we want to do some special processing. To do this we simply add a watch after creating the robot.

This is done by changing the constructor of the Bot Master by watching each created child:

7
1
public BotMaster() {
2
    for (int index = 0; index < 10; index++) {
3
        final ActorRef child = 
4
            getContext().actorOf(Props.create(AkkaBot.class));
5
        getContext().watch(child);
6
    }
7
}

And then handle the case of receiving a terminated message in the onReceive method:

13
1
public Receive createReceive() {
2
    return receiveBuilder()
3
            .match(StartChildBots.class, this::onStartChildBots)
4
            .match(Terminated.class, this::onChildTerminated)
5
            .build();
6
}
7
​
8
private void onChildTerminated(Terminated terminated) {
9
    System.out.println("Child has stopped, starting a new one");
10
    final ActorRef child = 
11
        getContext().actorOf(Props.create(AkkaBot.class));
12
    getContext().watch(child);
13
}

Now we need to make the child randomly fail. This can be done in Java by adding the field to the Move message handler in the JavaAkkaBot:

7
1
System.out.println(getSelf().path() + ": I am now moving " + direction.get());
2
​
3
final Random random = new java.util.Random();
4
final int nextInt = random.nextInt(10);
5
if ((nextInt % 2) == 0) {
6
    getContext().stop(getSelf());
7
}

For Scala, add the following to the Move message handler in the ScalaAkkaBot:

9
1
​
2
val random = scala.util.Random
3
​
4
println(s"${self.path} am now moving $direction")
5
if ((random.nextInt(10) % 10) == 0) {
6
  context.stop(self)
7
}
8
​
9
​

You can now see how the actors deal with failure.

Section 11

Further Learning

Now you have learned the basics of Akka actors, but there’s a lot more to it than just that. Actors can trivially be distributed across multiple nodes within a cluster, handle load balancing and sharding as well as persistence, or streaming using Akka Streams. If you want to learn more about what is in the toolkit, check out the Akka reference documentation at http://akka.io/docs/ and the Akka samples at https://github.com/akka/akka-samples.

http://www.slideshare.net/jboner/introducing-akka

Like This Refcard? Read More From DZone

related article thumbnail

DZone Article

The Good, the Bad, and the Ugly: Propagating Data Through Reactive Streams
related article thumbnail

DZone Article

How to Convert XLS to XLSX in Java
related article thumbnail

DZone Article

Automatic Code Transformation With OpenRewrite
related article thumbnail

DZone Article

Accelerating AI Inference With TensorRT
related refcard thumbnail

Free DZone Refcard

MQTT Essentials
related refcard thumbnail

Free DZone Refcard

Messaging and Data Infrastructure for IoT
related refcard thumbnail

Free DZone Refcard

Data Management for Industrial IoT
related refcard thumbnail

Free DZone Refcard

Edge Computing

ABOUT US

  • About DZone
  • Support and feedback
  • Community research
  • Sitemap

ADVERTISE

  • Advertise with DZone

CONTRIBUTE ON DZONE

  • Article Submission Guidelines
  • Become a Contributor
  • Core Program
  • Visit the Writers' Zone

LEGAL

  • Terms of Service
  • Privacy Policy

CONTACT US

  • 3343 Perimeter Hill Drive
  • Suite 100
  • Nashville, TN 37211
  • support@dzone.com

Let's be friends: