Basic Akka Actors

by
Tags: ,

Anyone working on concurrent and distributed applications on the JVM should be familiar with Akka. Akka is an implementation of the Actor model that can be used from Scala or Java applications to provide a concurrency model that is designed from the ground up for systems spanning multiple machines.

Defining an Actor

The key abstraction in Akka is the Actor, which provides behavior and can store state. In Akka, actors are guaranteed to be run in a single-threaded illusion, which means that the Akka framework takes care of threading issues while allowing us to focus on the behavior that needs to be implemented. Actors may only communicate with each other and the outside world by through messages. Any immutable object can be used as a message in Akka, so a simple String could be used as a message, but case objects and case classes are most often used for messages.

An actor in Akka is created by writing a class that extends from akka.actor.Actor and implements the abstract receive method. The receive method defines the behavior of the actor in response to receiving a message.

import akka.actor._
class LogActor extends Actor {
	var msgCount = 0
	def receive = {
		case msg =>
			msgCount += 1
			println(s"Received message $msg. Total handled = $msgCount")
	}
}

The receive method is defined to return Receive, which is actually a type alias for PartialFunction[Any, Unit]. A key point to understand is that the receive method is not executed to handle a message. Rather, the PartialFunction returned by receive is executed to handle a message.

Note that we have defined a mutable variable msgCount to keep track of the number of messages that have been handled. Although we normally use immutable types in Scala (and should in Java as well), we can and often do have mutable state in an actor. This is safe because the scope of the mutable variable is limited to the actor, and Akka guarantees that the actor code will be executed in a single-threaded illusion. However, it is very important not to let the mutable state escape the Actor. Any interactions with other actors or the outside world must still be through immutable messages.

Exercising an Actor

We can do some basic exploration of Akka with sbt and the Scala REPL. First, run sbt (or activator), then type in the following commands:

set scalaVersion := "2.11.6"
set libraryDependencies += "com.typesafe.akka" %% "akka-actor" % "2.3.9"

This will configure sbt to use Scala 2.11 and to have the core Akka library on the classpath. Now go to the Scala console by entering "console". This may take a couple of minutes if you haven't run sbt and downloaded dependencies previously. You should end up at a prompt that says "scala>".

Now use paste mode to insert the actor code above. Type ":pa" to enter paste mode, paste in the code (including the import statement), and then type CTRL-D to exit paste mode. If all goes well you should see

// Exiting paste mode, now interpreting.
import akka.actor._
defined class LogActor

Once we've defined an Actor, how do we put it to work? We need to create an ActorSystem, which is a container for a set of Actors.

scala> val system = ActorSystem()
system: akka.actor.ActorSystem = akka://default

Then, we can create an instance of our Actor using the actorOf method on the system.

scala> val ref = system.actorOf(Props(new LogActor()))
ref: akka.actor.ActorRef = Actor[akka://default/user/$a#352852783]

actorOf returns an ActorRef, which is what we can use to communicate with the actor. The ActorRef is only a proxy for the actor, however. The actual actor object is hidden within the actor system. Again, only messages can be used to communicate with the actor. We can send a message using the tell method, which is idiomatically defined as !.

scala> ref ! "Hello, World"
Received message Hello, World. Total handled = 1

Here we send the actor a message which is a String, and we see that the actor writes out the message and the number of messages handled. As we send more messages we should see the message count increase.

scala> ref ! 10
Received message 10. Total handled = 2
scala> ref ! Some("foo")
Received message Some(foo). Total handled = 3

When you're done with an ActorSystem, you must remember to shut it down by calling the shutdown method. If you don't the process will never exit.

scala> system.shutdown()

So that's it for this introduction to Akka. We created an Actor and showed that we can send messages to it and have it do some work. If this seems interesting, you might want to look at my previous post about FSM actors in Akka.