How to send and receive messages between Scala/Akka actors

This is an excerpt from the Scala Cookbook. This is Recipe 13.3, “How to communicate (send messages) between Scala/Akka actors.”

Problem

You’re building a Scala actor-based application and want to send messages between actors.

Solution

Actors should be sent immutable messages with the ! method.

When an Akka actor receives a message from another actor, it also receives an implicit reference named sender, and it can use that reference to send a message back to the originating actor.

The general syntax to send a message to an actor is:

actorInstance ! message

For example, if you have an Actor instance named car, you can send it a start message like this:

car ! "start"

In this case, the message is the String literal start. The car actor should receive this message in a match expression in its receive method, and from there it can send a message back to whoever sent the start message. A simplified version of a receive method for car might look like this:

def receive = {
    case "start" =>
         val result = tryToStart()
         sender ! result
    case _ => // do nothing
}

As mentioned, the sender instance is implicitly made available to your actor. If you just want to send a message back to the code that sent you a message, that’s all you have to do.

Discussion

To demonstrate a more complicated example of actors communicating, the following code shows how to send messages back and forth between Akka actors. It was inspired by the “Ping Pong” threading example in the book, The Java Programming Language (Addison-Wesley Professional), by Ken Arnold, James Gosling, and David Holmes:

import akka.actor._

case object PingMessage
case object PongMessage
case object StartMessage
case object StopMessage

class Ping(pong: ActorRef) extends Actor {
    var count = 0
    def incrementAndPrint { count += 1; println("ping") }
    def receive = {
        case StartMessage =>
            incrementAndPrint
            pong ! PingMessage
        case PongMessage =>
            incrementAndPrint
            if (count > 99) {
                sender ! StopMessage
                println("ping stopped")
                context.stop(self)
            } else {
                sender ! PingMessage
            }
        case _ => println("Ping got something unexpected.")
    }
}

class Pong extends Actor {
    def receive = {
      case PingMessage =>
          println(" pong")
          sender ! PongMessage
      case StopMessage =>
          println("pong stopped")
          context.stop(self)
      case _ => println("Pong got something unexpected.")
    }
}

object PingPongTest extends App {
    val system = ActorSystem("PingPongSystem")
    val pong = system.actorOf(Props[Pong], name = "pong")
    val ping = system.actorOf(Props(new Ping(pong)), name = "ping")
    // start the action
    ping ! StartMessage
    // commented-out so you can see all the output
    //system.shutdown
}

Actors should communicate by sending immutable messages between each other. In this case there are four messages, and they’re defined using case objects: PingMessage, PongMessage, StartMessage, and StopMessage.

The PingPongTest object performs the following work:

  1. Creates an ActorSystem.
  2. Creates pong, an instance of the Pong actor. (The pong object is actually an instance of ActorRef, though I loosely refer to it as an actor, or actor instance.) The Pong actor constructor does not require any arguments, so the no-args Props syntax is used.
  3. Creates ping, an instance of the Ping actor. The Ping actor constructor takes one argument, an ActorRef, so a slightly different version of the Props syntax is used.
  4. Starts the ping/pong action by sending a StartMessage to the ping actor. Once ping receives the StartMessage, the actors send messages back and forth between each other as fast as they can until the counter limit in ping is reached. Messages are sent using the usual ! method.

To get things started, the Ping class needs an initial reference to the Pong actor, but once the action starts, the two actors just send a PingMessage and PongMessage to each other using the sender references they implicitly receive, until the Ping actor count limit is reached. At that time, it sends a StopMessage to the Pong actor, and then both actors call their context.stop methods. The context object is implicitly available to all actors, and can be used to stop actors, among other uses.

In addition to demonstrating how to communicate between actors using immutable messages, this example provides several examples of an ActorRef. The ping and pong instances are ActorRef instances, as is sender.

A great thing about an ActorRef is that it hides the Actor instance from you. For instance, the Pong actor can’t directly execute ping.incrementAndPrint; the two actors can only send messages between each other. Although this seems limiting at first, once you understand the model, you’ll see that it’s a terrific way to safely implement concurrency in your applications.

Messages can also be sent between actors using the ? or ask methods, but those should be used only rarely. See Recipe 13.10, “Sending a Message to an Actor and Waiting for a Reply” for examples of those methods.

The Scala Cookbook

This tutorial is sponsored by the Scala Cookbook, which I wrote for O’Reilly:

You can find the Scala Cookbook at these locations:

Add new comment

The content of this field is kept private and will not be shown publicly.

Anonymous format

  • Allowed HTML tags: <em> <strong> <cite> <code> <ul type> <ol start type> <li> <pre>
  • Lines and paragraphs break automatically.
By submitting this form, you accept the Mollom privacy policy.