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:
- You’re familiar with Scala.
- You’re comfortable with SBT. (I used SBT 0.13.x with this code).
- 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:
- The SBT build.sbt file.
- The Scala code.
- 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:
- The SBT build.sbt file
- The Scala code
- 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:
cd
into the HelloRemote directory.- Type
sbt run
to start the remote actor system. - In a separate terminal window,
cd
into the HelloLocal directory. - 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 format used by the application.conf file is known as HOCON. You can learn more about it here: https://github.com/typesafehub/config
- You can learn more about what can go into the application.conf file here: http://doc.akka.io/docs/akka/snapshot/general/configuration.html
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.)