Akka actors scaling across JVM's and servers,

2019-09-06 16:13发布

问题:

I find the introductory example of Akka remoting supplied on the Akka landing page a bit hard to swallow as an introduction, and the length of documentation necessary for learning the ins and outs of remoting malstructured for introductory purposes.

Here below is the code from the mentioned example, and I'd like to ask for a delineation of what that code means with some fair context, while relating to the question of whether any actor can be messaged remotely as if it were local requiring only a mere change of configuration. Previous answers about this last bit may seem somewhat contradictory to current Akka documentation, whereas the documentation in itself is somewhat inconclusive about this very point.

// ------------------------------
// config on all machines
akka {
 actor {
   provider = akka.remote.RemoteActorRefProvider
   deployment {
     /greeter {
       remote = akka.tcp://MySystem@machine1:2552
     }
   }
 }
}

// ------------------------------
// define the greeting actor and the greeting message
case class Greeting(who: String) extends Serializable

class GreetingActor extends Actor with ActorLogging {
  def receive = {
    case Greeting(who) ⇒ log.info("Hello " + who)
  }
}

// ------------------------------
// on machine 1: empty system, target for deployment from machine 2
val system = ActorSystem("MySystem")

// ------------------------------
// on machine 2: Remote Deployment - deploying on machine1
val system = ActorSystem("MySystem")
val greeter = system.actorOf(Props[GreetingActor], name = "greeter")

// ------------------------------
// on machine 3: Remote Lookup (logical home of “greeter” is machine2, remote deployment is transparent)
val system = ActorSystem("MySystem")
val greeter = system.actorSelection("akka.tcp://MySystem@machine2:2552/user/greeter")
greeter ! Greeting("Sonny Rollins")

So an introductory explanation of this example code that also takes care of the key aspects mentioned above would be very helpful. An explanation that should hopefully enable picking an actors architecture that can easily scale within a single JVM and across JVM's and server boundaries, rather than going into experimentation mode for days if not more.

Thanks!

回答1:

Its probably easiest to explain the example you posted from bottom to top. At the end it shows vanilla location transparency to get an actor ref where we wouldn't hardcode the location string. With that minor modification we get:

// ------------------------------
// on machine 3: Remote Lookup (logical home of “greeter” is machine2, remote deployment is transparent)
val system = ActorSystem("MySystem")
val greeter = system.actorSelection(uri) // where uri is from config or some directory
greeter ! Greeting("Sonny Rollins")

How to lookup the location of an actor from configuration is shown at can a lookup an akka actor address from configuration? which sums up location transparency of using an actor. Once you have the ActorRef from the selection by uri you don't need to know whether its a local or a remote actor you can just message it.

Moving up the example the bit where it says:

// ------------------------------
// on machine 2: Remote Deployment - deploying on machine1
val system = ActorSystem("MySystem")
val greeter = system.actorOf(Props[GreetingActor], name = "greeter")

this is there to demonstrate local transparency of deploying an actor. The actorOf is a factory instantiation rather than a lookup hence it is a deployment. The name property deploys it to a uri path of "/user/greeter". If you had a single jvm configuration (e.g. empty configuration) and ran that in a single process you would get an local actor deployed into the single jvm. Yet the example comment says its running on machine2 and deploying into machine1; this is because of the config at each jvm which is further up the file:

akka {
 actor {
   provider = akka.remote.RemoteActorRefProvider
   deployment {
     /greeter {
       remote = akka.tcp://MySystem@machine1:2552
     }
   }
 }
}

That is saying that the user space "/user/greeter" is going to use RemoteActorRefProvider to remote deploy onto the actor system MySystem on machine1. If you took out that config then the sample code to deploy would not remote deploy but would local deploy. If it was a local deploy you could look it up using a local path without the full uri pointing to the system it is actually deployed onto. Regardless of whether its a local or remote deploy you get an ActorRef and message it.

Moving up the file some more at:

// ------------------------------
// on machine 1: empty system, target for deployment from machine 2
val system = ActorSystem("MySystem")

that is simply telling you that if you have config which will remote deploy actors at location "greeter" into "MySystem" on machine1 then you should start an actor system on machine1 with a name which matches the config. If not you will get connection or lookup errors trying to deploy or resolve "greeter" from other machines.

Finally the actor itself is location transparent as it has nothing about location baked into it as shown by:

// ------------------------------
// define the greeting actor and the greeting message
case class Greeting(who: String) extends Serializable

class GreetingActor extends Actor with ActorLogging {
  def receive = {
    case Greeting(who) ⇒ log.info("Hello " + who)
  }
}

So that actor has no idea whether it is a local actor or a remote actor or is running in a unit test using the akka Testkit. Effectively the configuration file shown deployed in all the machines is modifying the factory behaviours of the actor systems to deploy into the location "greeter" into one machine.