Implementing Actors in Akka

Reading Time: 2 minutes

After going through the previous blog (Introduction to Akka), we are now familiar with the term Akka. In this blog, I am going to talk about Actors and their implementation. So, let’s begin.

What Are Actors?

Actors were invented in 1973 by Carl Hewitt. Akka is an implementation of the Actor Model.

The Actor is the fundamental unit of computation embodying processing, storage, and communication. In Akka,

  • Processing is implemented as Behavior.
  • Storage is implemented as a State.
  • Communication is implemented as Messages.

Fundamental Concepts Of The Actor Model

Everything is an Actor. Each Actor has an address. When an Actor handles a message, it can:

  • Create new Actor(s).
  • Send message(s) to other Actor(s).
  • Change the behavior for handling the next message.

Anatomy Of An Actor

Each Actor is represented by an ActorRef. So, we never get access to an Actor instance directly. The Actor reference lets us send messages to the Actor.

Each Actor has a mailbox and a dispatcher. So, the ActorRef enqueues incoming messages in the mailbox. After that, the dispatcher schedules message processing. Only one message at a time is passed to an Actor.

Implementing An Actor

So to implement an Actor mix-in the Actor trait and for Akka’s logging facility mix-in the ActorLogging trait.

Behavior is simply a PartialFunction[Any, Unit]. It means:

  • An Actor can but doesn’t need to handle any message.
  • receive is a method defined in the Actor trait. It returns the Actor’s initial behavior. It means:
    • Messages are not handled by the receive method, but by its return value.
    • And, the behavior can be changed at runtime.

Unhandled Messages

As we know that Actors can choose to handle any message. But what happens if they choose not to handle a particular message?

So, by default:

  • An UnhandledMessage event is published to the event stream.
  • Or, a DeathPactException is thrown for an unhandled Terminated message.

So, a DeathPactException is thrown by an Actor that receives a Terminated(<someActor>) message that it doesn’t handle itself, effectively crashing the Actor and moving on to the supervisor.

Overriding the unhandled method could customize this problem. Example,

def unhandled(message: Any): Unit = ...

In order to log unhandled messages, set the configuration setting to on.

So, this is the basic implementation of an Actor. Later, we are going to talk more in detail about the Actors.