Akka 2.1 Remote: sharing an actor across systems
I am talking about remote actors in Akka 2.1, and I tried to adapt the counter example provided in the Types section . I have implemented a quick'n'dirty UI from the console to send ticks. And leave with a request (and showing the result) of the current account.
The idea is to start a node wizard which will start Counter Counter and some client node which will send messages to it via remote access. However, I would like to achieve this with customization and minimal code changes. Therefore, by changing the configuration, you can use local executors.
I found this blog post about a similar issue where it is necessary that all API calls go through a single actor even if there are many instances running.
I wrote a similar config, but I can't seem to get it to work. My current code does indeed use remote access, but it creates a new actor on the master for every new node, and I can't get it to connect to an existing actor without explicitly specifying its path (and ignoring the config point). However, this is not what I want, since the state cannot be shared between JVMs this way.
Full available code via git repo
This is my config file
akka {
actor {
provider = "akka.remote.RemoteActorRefProvider"
deployment {
/counter {
remote = "akka://ticker@127.0.0.1:2552"
}
}
}
remote {
transport = "akka.remote.netty.NettyRemoteTransport"
log-sent-messages = on
netty {
hostname = "127.0.0.1"
}
}
}
And full source
import akka.actor._
import akka.pattern.ask
import scala.concurrent.duration._
import akka.util.Timeout
import scala.util._
case object Tick
case object Get
class Counter extends Actor {
var count = 0
val id = math.random.toString.substring(2)
println(s"\nmy name is $id\ni'm at ${self.path}\n")
def log(s: String) = println(s"$id: $s")
def receive = {
case Tick =>
count += 1
log(s"got a tick, now at $count")
case Get =>
sender ! count
log(s"asked for count, replied with $count")
}
}
object AkkaProjectInScala extends App {
val system = ActorSystem("ticker")
implicit val ec = system.dispatcher
val counter = system.actorOf(Props[Counter], "counter")
def step {
print("tick or quit? ")
readLine() match {
case "tick" => counter ! Tick
case "quit" => return
case _ =>
}
step
}
step
implicit val timeout = Timeout(5.seconds)
val f = counter ? Get
f onComplete {
case Failure(e) => throw e
case Success(count) => println("Count is " + count)
}
system.shutdown()
}
I used sbt run
it in another window sbt run -Dakka.remote.netty.port=0
to launch it as well.
source to share
I found out that I can use some kind of sample. Remote Akka only allows deployment to remote systems (Can't find a way to have it look at remote only via config .. am I wrong here?).
So I can deploy a "scout" that will return an ActorRef. The managed code is available in the source repo under the "scout-hack" branch. Because it looks like a hack. I will still be grateful for a configuration based solution.
Actor
case object Fetch
class Scout extends Actor{
def receive = {
case Fetch => sender ! AkkaProjectInScala._counter
}
}
Counter-actor creation is now lazy
lazy val _counter = system.actorOf(Props[Counter], "counter")
So it only runs on the host (determined by the port) and can be selected that way
val counter: ActorRef = {
val scout = system.actorOf(Props[Scout], "scout")
val ref = Await.result(scout ? Fetch, timeout.duration) match {
case r: ActorRef => r
}
scout ! PoisonPill
ref
}
And full configuration
akka {
actor {
provider = "akka.remote.RemoteActorRefProvider"
deployment {
/scout {
remote = "akka://ticker@127.0.0.1:2552"
}
}
}
remote {
transport = "akka.remote.netty.NettyRemoteTransport"
log-sent-messages = on
netty {
hostname = "127.0.0.1"
}
}
}
EDIT : I also found a clean way: check the config for "counterPath" anf if actorFor (path) is present, otherwise create an actor. It's nice that you can enter the wizard at startup, and the code is much cleaner than the scout's, but he still has to decide what the weather will look for or create an actor. I think this cannot be avoided.
source to share
I tried your git project and it does work fine except for a compile error and that you should start an sbt session with a parameter -Dakka.remote.netty.port=0
in the jvm, not as a parameter run
.
You should also understand that you do not need to run Counter Counter in both processes. In this example, it is intended to be created from the client and deployed to the server (port 2552). You don't need to run it on the server. This should be enough to create a live system on the server for this example.
source to share