An Akka actors ‘remote’ example

While doing some crazy things with SARAH, I realized that the best way to solve a particular problem was to use remote Akka actors. I haven’t had the opportunity to work with Akka much since finishing the Scala Cookbook, so I dug around trying to find a simple Akka remote “Hello, world” example. Unable to find a good one, I read some stuff and created it myself.

Note: This article was originally written for Akka 2.1, and in May, 2015 I added some documentation at the end to update it for Akka 2.3.

The way my example works is that there are “local” and “remote” aspects to it. I set them up as two SBT projects underneath a main directory. I include a detailed discussion below on how to run the code, but in short, to get things running you first start the remote application in one JVM, and then start the local application in a second JVM. When the local application starts, it begins a short conversation with the remote.

If you want to jump in, you can clone my code from Github at this URL. Everything else in this article shows the code, and describes how it works.

Assumptions

Before going on, it’s important to note my assumptions about you, the reader. I assume:

  1. You’re familiar with Scala.
  2. You’re comfortable with SBT. (I used SBT 0.13.x with this code).
  3. You’ve used Akka actors locally. (If not, see my previous Akka tutorials.)

The “remote” application

As mentioned, this project consists of a local application and a remote application. You can think of the remote application as a “server” component. It’s contained in the directory named HelloRemote, and consists of three files:

  1. The SBT build.sbt file.
  2. The Scala code.
  3. An application.conf file to configure the actor system.

The directory structure looks like this:

HelloRemote/
|-- build.sbt
|-- src
    |-- main
    │   |-- java
    │   |-- resources
    │   │   +-- application.conf
    │   +-- scala
    │       +-- remote
    │           +-- HelloRemote.scala
    +-- test
        |-- java
        |-- resources
        +-- scala

Here’s the source code for HelloRemote.scala:

package remote

import akka.actor._

object HelloRemote extends App  {
  val system = ActorSystem("HelloRemoteSystem")
  val remoteActor = system.actorOf(Props[RemoteActor], name = "RemoteActor")
  remoteActor ! "The RemoteActor is alive"
}

class RemoteActor extends Actor {
  def receive = {
    case msg: String =>
        println(s"RemoteActor received message '$msg'")
        sender ! "Hello from the RemoteActor"
  }
}

Here’s the source code for application.conf:

# akka 2.1

akka {
  //loglevel = "DEBUG"
  actor {
    provider = "akka.remote.RemoteActorRefProvider"
   }
   remote {
     transport = "akka.remote.netty.NettyRemoteTransport"
     //log-sent-messages = on
     //log-received-messages = on
     netty {
       hostname = "127.0.0.1"
       port = 5150
     }
   }
}

An important thing to note from that configuration file is that the server listens on port 5150 (my preferred port ... something to do with Van Halen).

Finally, here are the contents of the build.sbt file:

name := "HelloRemote"

version := "1.0"

scalaVersion := "2.10.1"

resolvers += "Typesafe Repository" at "http://repo.typesafe.com/typesafe/releases/"

libraryDependencies ++= Seq(
  "com.typesafe.akka" %% "akka-actor" % "2.1.1",
  "com.typesafe.akka" %% "akka-remote" % "2.1.1"
)

(The latest version of Akka at the time of this publication is 2.2.3. I don’t remember why I used 2.1.1, other than the fact that I started this a few months ago.)

The “local” application

The local application is contained in the directory named HelloLocal, and consists of the same three files:

  1. The SBT build.sbt file
  2. The Scala code
  3. An application.conf file to configure the actor system

The directory structure looks like this:

HelloLocal/
|-- build.sbt
|-- src
    |-- main
    │   |-- java
    │   |-- resources
    │   │   +-- application.conf
    │   +-- scala
    │       +-- local
    │           +-- Local.scala
    +-- test
        |-- java
        |-- resources
        +-- scala

Here’s the source code for HelloLocal.scala:

package local

import akka.actor._

object Local extends App {

  implicit val system = ActorSystem("LocalSystem")
  val localActor = system.actorOf(Props[LocalActor], name = "LocalActor")  // the local actor
  localActor ! "START"                                                     // start the action

}

class LocalActor extends Actor {

  // create the remote actor (Akka 2.1 syntax)
  val remote = context.actorFor("akka://HelloRemoteSystem@127.0.0.1:5150/user/RemoteActor")
  var counter = 0

  def receive = {
    case "START" =>
        remote ! "Hello from the LocalActor"
    case msg: String =>
        println(s"LocalActor received message: '$msg'")
        if (counter < 5) {
            sender ! "Hello back to you"
            counter += 1
        }
  }
}

Note how the remote actor reference is created; along with the configuration files, it’s one of the few secret ingredients to this recipe (assuming you already know how to use actors locally, i.e., in one JVM).

Here’s the source code for this project’s application.conf file:

# akka 2.1

akka {
  //loglevel = "DEBUG"
  actor {
    provider = "akka.remote.RemoteActorRefProvider"
  }
  remote {
    transport = "akka.remote.netty.NettyRemoteTransport"
    //log-sent-messages = on
    //log-received-messages = on
    netty {
      hostname = "127.0.0.1"
      port = 0
    }
  }
}

Finally, here are the contents of the build.sbt file:

name := "HelloLocal"

version := "1.0"

scalaVersion := "2.10.1"

resolvers += "Typesafe Repository" at "http://repo.typesafe.com/typesafe/releases/"

libraryDependencies ++= Seq(
  "com.typesafe.akka" %% "akka-actor" % "2.1.1",
  "com.typesafe.akka" %% "akka-remote" % "2.1.1"
)

Running the code

Follow these steps to run the code:

  1. cd into the HelloRemote directory.
  2. Type sbt run to start the remote actor system.
  3. In a separate terminal window, cd into the HelloLocal directory.
  4. Type sbt run to start the local actor system.

When the local actor system starts, it will send an initial message to the remote actor system. The remote actor will send a reply through its sender reference, and this will continue five times. When the action stops, stop each system by pressing Ctrl-C.

Notes

A few notes:

The code on Github

As mentioned earlier, you can clone my project from Github at this URL:

There’s a short README file there as well.

Problems

If you have any problems with this code, edit the application.conf file in the src/main/resources directory of each project, and remove the comments from the debug-related lines. That may help to show any problems.

Update for Akka 2.3.x

I learned last night that the Akka 2.3.x syntax has changed significantly. I don’t have time to rewrite this article today, so I’ll just add some quick notes about it.

First, the application.conf file for the remote application now looks like this:

akka {
  loglevel = "INFO"
  actor {
    provider = "akka.remote.RemoteActorRefProvider"
  }
  remote {
    enabled-transports = ["akka.remote.netty.tcp"]
    netty.tcp {
      hostname = "127.0.0.1"
      port = 5150
    }
    log-sent-messages = on
    log-received-messages = on
  }
}

The application.conf for the local (client) looks like this:

akka {
  loglevel = "INFO"

  actor {
    provider = "akka.remote.RemoteActorRefProvider"
  }

  remote {
    enabled-transports = ["akka.remote.netty.tcp"]
    netty.tcp {
      hostname = "127.0.0.1"
      port = 0
    }

    log-sent-messages = on
    log-received-messages = on
  }

}

And the URL that the local system should use to connect to the remote actor looks like this:

val remoteActor = context.actorFor("akka.tcp://HelloRemoteSystem@127.0.0.1:5150/user/RemoteActor")

In the build.sbt file for both the local and remote systems you’ll also need to update to the Akka 2.3 libraries, something like this:

libraryDependencies ++= Seq(
    "com.typesafe.akka" %% "akka-actor" % "2.3.2",
    "com.typesafe.akka" %% "akka-remote" % "2.3.2"
)

I think that’s all the changes I had to make after following the earlier parts of this tutorial.

Summary

In summary, if you’re looking for a simple Akka remote “Hello, world” example, I hope you find this code useful.

I want to keep this example simple, so I intend to leave this code as is (unless anyone reports any problems). If/when I have some more free time I’ll create another project with a few more “real world” enhancements to this. (But alas, free time is scarce these days.)