Actors In Akka

Reading Time: 4 minutes

Overview

In this blog, we will learn about actors in Akka.

Before talking about Actors in Akka we need to know what is Akka.

Akka is a toolkit for building highly concurrent, distributed, and resilient message-driven applications for Java and Scala. In our case we talk about use of Akka In Scala.

What is an Actor in Akka?

An actor is essentially nothing more than an object that receives messages and takes actions to handle them. It is decoupled from the source of the message.

Its only responsibility is to properly recognize the type of message it has received and take action accordingly.

Upon receiving a message, an actor may take one or more of the following actions:

  • Execute some operations itself (such as performing calculations, persisting data, calling an external web service, and so on)
  • Forward the message, or a derived message, to another actor
  • Instantiate a new actor and forward the message to it

Alternatively, the actor may choose to ignore the message entirely (i.e., it may choose inaction).

To implement an actor, it is necessary to extend the akka.actor.Actor trait and implement the receive method. An actor’s receive method is invoked (by Akka) when a message is sent to that actor. Its typical implementation consists of pattern matching.

As shown in the following Akka example, to identify the message type and react accordingly:

import akka.actor.typed.scaladsl.Behaviors
import akka.actor.typed.scaladsl.LoggerOps
import akka.actor.typed.{ ActorRef, ActorSystem, Behavior }

With these in place we can define our first Actor, and it will say hello!

object HelloWorld {
  final case class Greet(whom: String, replyTo: ActorRef[Greeted])
  final case class Greeted(whom: String, from: ActorRef[Greet])

  def apply(): Behavior[Greet] = Behaviors.receive { (context, message) =>
    context.log.info("Hello {}!", message.whom)
    message.replyTo ! Greeted(message.whom, context.self)
    Behaviors.same
  }
}

The Actors System In Akka

Taking a complex problem and recursively splitting it into smaller sub-problems is a sound problem solving technique in general. This approach can be particularly beneficial in computer science (consistent with the Single Responsibility Principle).

As it tends to yield clean, modularized code, with little or no redundancy, that is relatively easy to maintain.

In an actor-based design, use of this technique facilitates the logical organization of actors into a hierarchical structure known as an Actor System.

The actor system provides the infrastructure through which actors interact with one another.

In Akka, the only way to communicate with an actor is through an ActorRef. An ActorRef represents a reference to an actor that precludes other objects from directly accessing or manipulating that actor’s internals and state. Messages may be sent to an actor via an ActorRef using one of the following syntax protocols:

  • ! (“tell”) – sends the message and returns immediately
  • ? (“ask”) – sends the message and returns a Future representing a possible reply

Each actor has a mailbox to which its incoming messages are delivered. There are multiple mailbox implementations from which to choose, with the default implementation being FIFO.

An actor contains many instance variables to maintain state while processing multiple messages. Akka ensures that each instance of an actor runs in its own lightweight thread and that messages are processed one at a time.

In this way, each actor’s state can be reliably maintained without the developer needing to explicitly worry about synchronization or race conditions.

Some useful information of Akka Actors

Each actor is provided with the following useful information for performing its tasks via the Akka Actor API:

  • sender: an ActorRef to the sender of the message currently being processed
  • context: information and methods relating to the context within which the actor is running (includes, for example, an actorOf method for instantiating a new actor)
  • supervisionStrategy: defines the strategy to be used for recovering from errors
  • self: the ActorRef for the actor itself
Akka ensures that each instance of an actor runs in its own lightweight thread and that messages are processed one at a time. In this way, each actor's state can be reliably maintained without the developer needing to explicitly worry about synchronization or race conditions.

To help tie these tutorials together, let’s consider a simple example of counting the number of words in a text file.

For purposes of our Akka example, we’ll decompose the problem into two subtasks;

  • a “child” task of counting the number of words on a single line and,
  • a “parent” task of summing those per-line word counts to get the total number of words in the file.

The parent actor will load each line from the file and then delegate to a child actor the task of counting the words in that line.

When the child is done, it will send a message back to the parent with the result.

The parent will receive the messages with the word counts (for each line) and keep a counter for the total number of words in the entire file, which it will then return to its invoker upon completion.

Conclusion

In this blog, we talked about actors in akka.

Akka ensures that each instance of an actor runs in its own lightweight thread and that messages are processed one at a time. In this way, each actor’s state can be reliably maintained without the developer needing to explicitly worry about synchronization or race conditions.

References

knoldus

Written by 

My name is Harshal Dubey. I am working as a Software Intern in Scala Studio at Knoldus. My hobbies are playing volleyball, football and Travelling.