Getting Started with Actor Based Programming Using Scala and Akka

Building concurrent software is difficult, especially when directly managing Threads. Handling synchronization with MutExes, memory barriers, and condition variables is rife with error. This is because under-synchronizing your code will lead to race conditions, while over-synchronizing your code lowers performance. The good news is that there is a better way, known as the Actor Pattern. Made popular with Erlang, the Actor Pattern makes it easy to separate concerns into isolated actors that allow for safe concurrency. Akka brings the same pattern to Scala, so you can build concurrent software faster, and safer, than ever before.

Let’s Get Going

Let’s kick things off by building an application that lets patrons order beer from a Texas themed bar, called “Howdy Akka”. Akka makes things easy with SBT by adding Akka as a dependency in our build file. To do so, just open your editor of choice and add the following into a file named build.sbt.

What is SBT?

Simple Build Tool or SBT, is an automated build tool for Scala, Java, and other JVM based languages. It is what we will be using to manage dependencies and compile our program for execution.

build.sbt

name := "howdy-akka"

version := "1.0"

scalaVersion := "2.10.4"

libraryDependencies ++= Seq(
  "com.typesafe.akka" %% "akka-actor" % "2.3.1"
)

Next, let’s make our main object called HowdyAkka and subclass it off of App. For our first example, we are just going to output text to the screen.

src/main/scala/HowdyAkka.scala

object HowdyAkka extends App {
  println("Howdy Akka")
}

Once we have our main class set up, we can run our program by typing sbt in our application’s directory, and then typing the run command. The first thing SBT will do is load all the required dependencies, including the specified version of Scala. Then, SBT will build and execute our HowdyAkka object, yielding the following console output.

Console Output

Running HowdyAkka
Howdy Akka
[success] Total time: 0 s, completed Mar 27, 2014 4:18:15 PM

Looking good. Now, lets start diving into some Actor-based concepts.

Your First Performance

If we are going to use the Actor Pattern, we need to start making Actors and Messages. First thing is we must do is import the necessary classes and objects from Akka into our HowdyAkka.scala file. Next, we will create a very simple Message, a case object called Pint that our Actors will pass amongst each other. Then, were we make a new Actor, a class called Person that is subclassed from Actor, and extended with the ActorLogging trait.

Our Person Actor needs to implement at least one method, called receive, which takes a partial function that pattern matches the Messages received by the Actor. In this case, we are only expecting one Message, the Pint case object that we defined earlier. When a Person Actor gets a Pint Message, they will log the event and wait for the next Message.

Then, we setup Akka by creating a new system value via the ActorSystem Object. This system value is the root object of our Akka Application, and is what all of our Actors will belong to. We can attach a new Actor, called alice, to the system using the actorOf method, which takes the Property of a Person Actor, and an identifier. We then send a Pint Message to our new alice Actor using the ! operator. Finally, we can tell the system to shutdown, so the program will close.

src/main/scala/HowdyAkka.scala

import akka.actor.{ActorSystem, ActorLogging, Actor, Props}

case object Pint

class Person extends Actor with ActorLogging {
  def receive = {
    case Pint => log.info("Thanks for the pint")
  }
}

object HowdyAkka extends App {
  val system = ActorSystem("howdy-akka")

  val alice = system.actorOf(Props(new Person), "alice")

  alice ! Pint

  system.shutdown()
}

Once you have made your changes to the HowdyAkka.scala file, type the run command in the SBT window and watch the log output.

Console Output

Running HowdyAkka
[03/27/2014 16:31:17.332] [akka://howdy-akka/user/alice] thanks for the pint!
[success] Total time: 2 s, completed Mar 27, 2014 4:31:17 PM

Fantastic, there are a few things going on here. First, we are seeing our output logged to the screen with the time of the event. Also, we notice that our alice Actor has a path with the identifier we chose earlier. Within Akka, every Actor has a unique URL, which you can use to find any Actor in your system, be it on the same computer or a data center an ocean away.

Getting Multiple Actors on the Stage

For this to be a real bar, we need a bar tender. Lets make a new BarTender Actor that knows how to keep the pints moving. At this bar, the tender will receive a Ticket and send back a FullPint. Notice that we are using the special sender method, which automatically references the Actor who sent the Message. The Person who receives the FullPint will then make into an EmptyPint (someone has to do the hard work) and send an EmptyPint Message back to the BarTender. For now, there is only 1 pint, and when the BarTender gets an EmptyPint Message, he will alert the sending Person that the fun is over, and close the bar down. We can get to the system value and call shutdown via the special context method that is also included with our Actor, similar to the sender method.

To kick things off, we need to send the initial Ticket to the BarTender, who we’ll call zed, on behalf of alice using the tell method. In Akka, the tell method and ! operator do exactly the same thing. However, we can pass the Sender Context of the Message as the second parameter to the tell method, which helps kick things off. Finially, we also tell Akka to ensure the application finished execution by using the awaitTermination method on our system value. Keep this in mind, as if you do not tell Akka to keep the system running before a shutdown, it might close prematurely.

src/main/scala/HowdyAkka.scala

import akka.actor.{ActorSystem, ActorLogging, Actor, Props}

case object Ticket
case object FullPint
case object EmptyPint

class BarTender extends Actor with ActorLogging {
  def receive = {
    case Ticket =>
      log.info("1 pint coming right up")

      Thread.sleep(1000)

      log.info("Your pint is ready, here you go")

      sender ! FullPint

    case EmptyPint =>
      log.info("I think you're done for the day")

      context.system.shutdown()
  }
}

class Person extends Actor with ActorLogging {
  def receive = {
    case FullPint =>
      log.info("I'll make short work of this")

      Thread.sleep(1000)

      log.info("I'm ready for the next")

      sender ! EmptyPint
  }
}

object HowdyAkka extends App {
  val system = ActorSystem("howdy-akka")

  val zed = system.actorOf(Props(new BarTender), "zed")

  val alice = system.actorOf(Props(new Person), "alice")

  zed.tell(Ticket, alice)

  system.awaitTermination()
}

Never Use Thread.sleep in Production

In this example, we are using Thread.sleep to have our program halt operation for a bit of time. However in production, there should never be a reason to use Thread.sleep. If you find yourself needing an operation to happen in the future, take a look at the Akka Scheduler instead.

Once we put our new HowdyAkka system together, run your program with SBT and check your output.

Console Output

Running HowdyAkka
[03/27/2014 16:58:56.218] [akka://howdy-akka/user/zed] 1 pint coming right up
[03/27/2014 16:58:57.220] [akka://howdy-akka/user/zed] Your pint is ready, here you go
[03/27/2014 16:58:57.221] [akka://howdy-akka/user/alice] I'll make short work of this
[03/27/2014 16:58:58.222] [akka://howdy-akka/user/alice] I'm ready for the next
[03/27/2014 16:58:58.223] [akka://howdy-akka/user/zed] I think you're done for the day
[success] Total time: 3 s, completed Mar 27, 2014 4:58:58 PM

Looking good, you just made your first multi-actor program with Akka. Lets kick things up one more notch.

And The Crowd Goes Wild

Our program is starting to get a bit more complex, so it makes sense to move each Actor into its own file. Furthermore, we want our BarTender to handle multiple people with multiple tickets, each wanting to drink multiple pints. With Akka, this is easy peasy.

First, lets convert our Ticket, FullPint and EmptyPint Messages into case classes that take either a quantity or number. This will allow each Person to tell the BarTender the number of pints they want, while allowing the BarTender to keep track of which pints are being sent and received.

src/main/scala/HowdyAkka.scala

import akka.actor.{ActorSystem, Props}

case class Ticket(quantity: Int)
case class FullPint(number: Int)
case class EmptyPint(number: Int)

object HowdyAkka extends App {
  val system = ActorSystem("howdy-akka")

  val zed = system.actorOf(Props(new BarTender), "zed")

  val alice   = system.actorOf(Props(new Person), "alice")
  val bob     = system.actorOf(Props(new Person), "bob")
  val charlie = system.actorOf(Props(new Person), "charlie")

  zed.tell(Ticket(2), alice)
  zed.tell(Ticket(3), bob)
  zed.tell(Ticket(1), charlie)

  system.awaitTermination()
}

The Person Actor stays simple, they receive a FullPint Message and send an EmptyPint Message back. We will just log some additional information to the screen to see how everyone is faring.

src/main/scala/Person.scala

import akka.actor.{ActorLogging, Actor}

class Person extends Actor with ActorLogging {
  def receive = {
    case FullPint(number) =>
      log.info(s"I'll make short work of pint $number")

      Thread.sleep(1000)

      log.info(s"Done, here is the empty glass for pint $number")

      sender ! EmptyPint(number)
  }
}

Finally, our BarTender needs to know how many pints are out in the open, which can be saved with our total variable. Notice how we are able to safely use a variable in our Actors, because they run in blissful isolation. If we were using Thread based concurrency primitives, we would need to synchronize our total variable with a MutEx. But with Akka, making concurrent software is much easier, thanks to the Actor Pattern.

Next, we pattern match our Message just as before, but we can also get the quantity of tickets sent, and use for comprehension to send each new FullPint to their correct recipient, and keep track of which pint it is for that specific Person. When the EmptyPint Messages start rolling in, we can see how many pints are left, and close up shop accordingly when everyone is finished.

src/main/scala/BarTender.scala

import akka.actor.{ActorLogging, Actor}

class BarTender extends Actor with ActorLogging {
  var total = 0

  def receive = {
    case Ticket(quantity) =>
      total = total + quantity

      log.info(s"I'll get $quantity pints for [${sender.path}]")

      for (number <- 1 to quantity) {
        log.info(s"Pint $number is coming right up for [${sender.path}]")

        Thread.sleep(1000)

        log.info(s"Pint $number is ready, here you go [${sender.path}]")

        sender ! FullPint(number)
      }

    case EmptyPint(number) =>
      total match {
        case 1 =>
          log.info("Ya'll drank those pints quick, time to close up shop")

          context.system.shutdown()

        case n =>
          total = total - 1

          log.info(s"You drank pint $number quick, but there are still $total pints left")
      }
  }
}

Once our changes are made, run the program within SBT and check the output. It should be interesting.

Console Output

Running HowdyAkka
[03/27/2014 17:27:44.152] [akka://howdy-akka/user/zed] I'll get 2 pints for [akka://howdy-akka/user/alice]
[03/27/2014 17:27:44.152] [akka://howdy-akka/user/zed] Pint 1 is coming right up for [akka://howdy-akka/user/alice]
[03/27/2014 17:27:45.153] [akka://howdy-akka/user/zed] Pint 1 is ready, here you go [akka://howdy-akka/user/alice]
[03/27/2014 17:27:45.154] [akka://howdy-akka/user/zed] Pint 2 is coming right up for [akka://howdy-akka/user/alice]
[03/27/2014 17:27:45.154] [akka://howdy-akka/user/alice] I'll make short work of pint 1
[03/27/2014 17:27:46.156] [akka://howdy-akka/user/alice] Done, here is the empty glass for pint 1
[03/27/2014 17:27:46.156] [akka://howdy-akka/user/zed] Pint 2 is ready, here you go [akka://howdy-akka/user/alice]
[03/27/2014 17:27:46.156] [akka://howdy-akka/user/zed] I'll get 3 pints for [akka://howdy-akka/user/bob]
[03/27/2014 17:27:46.156] [akka://howdy-akka/user/zed] Pint 1 is coming right up for [akka://howdy-akka/user/bob]
[03/27/2014 17:27:46.157] [akka://howdy-akka/user/alice] I'll make short work of pint 2
[03/27/2014 17:27:47.157] [akka://howdy-akka/user/zed] Pint 1 is ready, here you go [akka://howdy-akka/user/bob]
[03/27/2014 17:27:47.157] [akka://howdy-akka/user/zed] Pint 2 is coming right up for [akka://howdy-akka/user/bob]
[03/27/2014 17:27:47.157] [akka://howdy-akka/user/bob] I'll make short work of pint 1
[03/27/2014 17:27:47.157] [akka://howdy-akka/user/alice] Done, here is the empty glass for pint 2
[03/27/2014 17:27:48.158] [akka://howdy-akka/user/zed] Pint 2 is ready, here you go [akka://howdy-akka/user/bob]
[03/27/2014 17:27:48.158] [akka://howdy-akka/user/bob] Done, here is the empty glass for pint 1
[03/27/2014 17:27:48.158] [akka://howdy-akka/user/bob] I'll make short work of pint 2
[03/27/2014 17:27:48.158] [akka://howdy-akka/user/zed] Pint 3 is coming right up for [akka://howdy-akka/user/bob]
[03/27/2014 17:27:49.158] [akka://howdy-akka/user/bob] Done, here is the empty glass for pint 2
[03/27/2014 17:27:49.158] [akka://howdy-akka/user/zed] Pint 3 is ready, here you go [akka://howdy-akka/user/bob]
[03/27/2014 17:27:49.158] [akka://howdy-akka/user/zed] I'll get 1 pints for [akka://howdy-akka/user/charlie]
[03/27/2014 17:27:49.158] [akka://howdy-akka/user/zed] Pint 1 is coming right up for [akka://howdy-akka/user/charlie]
[03/27/2014 17:27:49.158] [akka://howdy-akka/user/bob] I'll make short work of pint 3
[03/27/2014 17:27:50.159] [akka://howdy-akka/user/bob] Done, here is the empty glass for pint 3
[03/27/2014 17:27:50.159] [akka://howdy-akka/user/zed] Pint 1 is ready, here you go [akka://howdy-akka/user/charlie]
[03/27/2014 17:27:50.159] [akka://howdy-akka/user/zed] You drank pint 1 quick, but there are still 5 pints left
[03/27/2014 17:27:50.159] [akka://howdy-akka/user/charlie] I'll make short work of pint 1
[03/27/2014 17:27:50.159] [akka://howdy-akka/user/zed] You drank pint 2 quick, but there are still 4 pints left
[03/27/2014 17:27:50.159] [akka://howdy-akka/user/zed] You drank pint 1 quick, but there are still 3 pints left
[03/27/2014 17:27:50.159] [akka://howdy-akka/user/zed] You drank pint 2 quick, but there are still 2 pints left
[03/27/2014 17:27:50.159] [akka://howdy-akka/user/zed] You drank pint 3 quick, but there are still 1 pints left
[03/27/2014 17:27:51.160] [akka://howdy-akka/user/charlie] Done, here is the empty glass for pint 1
[03/27/2014 17:27:51.160] [akka://howdy-akka/user/zed] Ya'll drank those pints quick, time to close up shop
[success] Total time: 8 s, completed Mar 27, 2014 17:27:52 PM

There definitely is a good deal of concurrency going on here, very happy concurrency.

Summary

This was a very quick tour of the basics of Scala, Akka, and Actor based concurrency. But, we covered a lot as well.

First, we made a simple Actor that received a Message and logged output. We then created multiple Actors and had them communicate with each other using structured Messages. Finally, we let our BarTender Actor manage state while interacting with multiple Person Actors in a safe, concurrent fashion. Through this tutorial, we have also seen that building concurrent applications with Actors is much easier, and safer, than with Threads. This added safety made it possible to not worry about synchronization, and we were instead able to focus on the business implications of our entities.