diff --git a/akka-actor/src/main/scala/akka/actor/ActorRefProvider.scala b/akka-actor/src/main/scala/akka/actor/ActorRefProvider.scala index 532395c172..cdddf71d7b 100755 --- a/akka-actor/src/main/scala/akka/actor/ActorRefProvider.scala +++ b/akka-actor/src/main/scala/akka/actor/ActorRefProvider.scala @@ -134,6 +134,14 @@ trait ActorRefProvider { * is usually initiated by stopping the guardian via ActorSystem.stop(). */ def terminationFuture: Future[Unit] + + /** + * Obtain the address which is to be used within sender references when + * sending to the given other address or none if the other address cannot be + * reached from this system (i.e. no means of communication known; no + * attempt is made to verify actual reachability). + */ + def getExternalAddressFor(addr: Address): Option[Address] } /** @@ -529,6 +537,8 @@ class LocalActorRefProvider( new RoutedActorRef(system, props.withRouter(d.routerConfig), supervisor, path) } } + + def getExternalAddressFor(addr: Address): Option[Address] = if (addr == rootPath.address) Some(addr) else None } class LocalDeathWatch(val mapSize: Int) extends DeathWatch with ActorClassification { diff --git a/akka-actor/src/main/scala/akka/actor/Address.scala b/akka-actor/src/main/scala/akka/actor/Address.scala index 87c1af897b..d182a0e3b4 100644 --- a/akka-actor/src/main/scala/akka/actor/Address.scala +++ b/akka-actor/src/main/scala/akka/actor/Address.scala @@ -45,6 +45,7 @@ final case class Address(protocol: String, system: String, host: Option[String], object Address { def apply(protocol: String, system: String) = new Address(protocol, system) + def apply(protocol: String, system: String, host: String, port: Int) = new Address(protocol, system, Some(host), Some(port)) } object RelativeActorPath { diff --git a/akka-actor/src/main/scala/akka/actor/Deployer.scala b/akka-actor/src/main/scala/akka/actor/Deployer.scala index 04cefda6db..85225bda4d 100644 --- a/akka-actor/src/main/scala/akka/actor/Deployer.scala +++ b/akka-actor/src/main/scala/akka/actor/Deployer.scala @@ -33,6 +33,7 @@ final case class Deploy( def this(routing: RouterConfig) = this("", ConfigFactory.empty, routing) def this(routing: RouterConfig, scope: Scope) = this("", ConfigFactory.empty, routing, scope) + def this(scope: Scope) = this("", ConfigFactory.empty, NoRouter, scope) /** * Do a merge between this and the other Deploy, where values from “this” take diff --git a/akka-docs/general/remoting.rst b/akka-docs/general/remoting.rst index a6a4b73910..4fcd680b24 100644 --- a/akka-docs/general/remoting.rst +++ b/akka-docs/general/remoting.rst @@ -41,11 +41,16 @@ guarantee!). How is Remoting Used? --------------------- -We took the idea of transparency to the limit in that there is no API for the -remoting layer of Akka: it is purely driven by configuration. Just write your -application according to the principles outlined in the previous sections, then -specify remote deployment of actor sub-trees in the configuration file. This -way, your application can be scaled out without having to touch the code. +We took the idea of transparency to the limit in that there is nearly no API +for the remoting layer of Akka: it is purely driven by configuration. Just +write your application according to the principles outlined in the previous +sections, then specify remote deployment of actor sub-trees in the +configuration file. This way, your application can be scaled out without having +to touch the code. The only piece of the API which allows programmatic +influence on remote deployment is that :class:`Props` contain a field which may +be set to a specific :class:`Deploy` instance; this has the same effect as +putting an equivalent deployment into the configuration file (if both are +given, configuration file wins). Marking Points for Scaling Up with Routers ------------------------------------------ diff --git a/akka-docs/java/code/akka/docs/remoting/RemoteDeploymentDocTest.scala b/akka-docs/java/code/akka/docs/remoting/RemoteDeploymentDocTest.scala new file mode 100644 index 0000000000..9290b7c897 --- /dev/null +++ b/akka-docs/java/code/akka/docs/remoting/RemoteDeploymentDocTest.scala @@ -0,0 +1,8 @@ +/** + * Copyright (C) 2009-2012 Typesafe Inc. + */ +package akka.docs.remoting + +import org.scalatest.junit.JUnitSuite + +class RemoteDeploymentDocTest extends RemoteDeploymentDocTestBase with JUnitSuite \ No newline at end of file diff --git a/akka-docs/java/code/akka/docs/remoting/RemoteDeploymentDocTestBase.java b/akka-docs/java/code/akka/docs/remoting/RemoteDeploymentDocTestBase.java new file mode 100644 index 0000000000..373bcf0c44 --- /dev/null +++ b/akka-docs/java/code/akka/docs/remoting/RemoteDeploymentDocTestBase.java @@ -0,0 +1,46 @@ +/** + * Copyright (C) 2009-2012 Typesafe Inc. + */ +package akka.docs.remoting; + +import org.junit.AfterClass; +import org.junit.BeforeClass; +import org.junit.Test; + +//#import +import akka.actor.ActorRef; +import akka.actor.Address; +import akka.actor.AddressExtractor; +import akka.actor.Deploy; +import akka.actor.Props; +import akka.actor.ActorSystem; +import akka.remote.RemoteScope; +//#import + +public class RemoteDeploymentDocTestBase { + + static ActorSystem system; + + @BeforeClass + public static void init() { + system = ActorSystem.create(); + } + + @AfterClass + public static void cleanup() { + system.shutdown(); + } + + @Test + public void demonstrateDeployment() { + //#make-address + Address addr = new Address("akka", "sys", "host", 1234); + addr = AddressExtractor.parse("akka://sys@host:1234"); // the same + //#make-address + //#deploy + ActorRef ref = system.actorOf(new Props(RemoteDeploymentDocSpec.Echo.class).withDeploy(new Deploy(new RemoteScope(addr)))); + //#deploy + assert ref.path().address().equals(addr); + } + +} \ No newline at end of file diff --git a/akka-docs/java/remoting.rst b/akka-docs/java/remoting.rst index 05101497e1..bb7c12b199 100644 --- a/akka-docs/java/remoting.rst +++ b/akka-docs/java/remoting.rst @@ -36,8 +36,12 @@ to your ``application.conf`` file:: As you can see in the example above there are four things you need to add to get started: * Change provider from ``akka.actor.LocalActorRefProvider`` to ``akka.remote.RemoteActorRefProvider`` -* Add host name - the machine you want to run the actor system on -* Add port number - the port the actor system should listen on +* Add host name - the machine you want to run the actor system on; this host + name is exactly what is passed to remote systems in order to identify this + system and consequently used for connecting back to this system if need be, + hence set it to a reachable IP address or resolvable name in case you want to + communicate across the network. +* Add port number - the port the actor system should listen on, set to 0 to have it chosen automatically The example above only illustrates the bare minimum of properties you have to add to enable remoting. There are lots of more properties that are related to remoting in Akka. We refer to the following @@ -63,7 +67,7 @@ Creating Actors Remotely ^^^^^^^^^^^^^^^^^^^^^^^^ The configuration below instructs the system to deploy the actor "retrieval” on the specific host "app@10.0.0.1". -The "app" in this case refers to the name of the ``ActorSystem``:: +The "app" in this case refers to the name of the ``ActorSystem`` (only showing deployment section):: akka { actor { @@ -88,6 +92,27 @@ As you can see from the example above the following pattern is used to find an ` akka://@:/ +Programmatic Remote Deployment +------------------------------ + +To allow dynamically deployed systems, it is also possible to include +deployment configuration in the :class:`Props` which are used to create an +actor: this information is the equivalent of a deployment section from the +configuration file, and if both are given, the external configuration takes +precedence. + +With these imports: + +.. includecode:: code/akka/docs/remoting/RemoteDeploymentDocTestBase.java#import + +and a remote address like this: + +.. includecode:: code/akka/docs/remoting/RemoteDeploymentDocTestBase.java#make-address + +you can advise the system to create a child on that remote node like so: + +.. includecode:: code/akka/docs/remoting/RemoteDeploymentDocTestBase.java#deploy + Serialization ^^^^^^^^^^^^^ @@ -122,9 +147,12 @@ the two given target nodes. Description of the Remoting Sample ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -The sample application included with the Akka sources demonstrates both, remote -deployment and look-up of remote actors. First, let us have a look at the -common setup for both scenarios (this is ``common.conf``): +There is a more extensive remote example that comes with the Akka distribution. +Please have a look here for more information: `Remote Sample +`_ +This sample demonstrates both, remote deployment and look-up of remote actors. +First, let us have a look at the common setup for both scenarios (this is +``common.conf``): .. includecode:: ../../akka-samples/akka-sample-remote/src/main/resources/common.conf diff --git a/akka-docs/scala/code/akka/docs/remoting/RemoteDeploymentDocSpec.scala b/akka-docs/scala/code/akka/docs/remoting/RemoteDeploymentDocSpec.scala new file mode 100644 index 0000000000..0a8785444f --- /dev/null +++ b/akka-docs/scala/code/akka/docs/remoting/RemoteDeploymentDocSpec.scala @@ -0,0 +1,52 @@ +/** + * Copyright (C) 2009-2012 Typesafe Inc. + */ +package akka.docs.remoting + +import akka.actor.{ ExtendedActorSystem, ActorSystem, Actor, ActorRef } +import akka.testkit.{ AkkaSpec, ImplicitSender } +//#import +import akka.actor.{ Props, Deploy, Address, AddressExtractor } +import akka.remote.RemoteScope +//#import + +object RemoteDeploymentDocSpec { + + class Echo extends Actor { + def receive = { + case x ⇒ sender ! self + } + } + +} + +class RemoteDeploymentDocSpec extends AkkaSpec(""" + akka.actor.provider = "akka.remote.RemoteActorRefProvider" + akka.remote.netty.port = 0 +""") with ImplicitSender { + + import RemoteDeploymentDocSpec._ + + val other = ActorSystem("remote", system.settings.config) + val address = other.asInstanceOf[ExtendedActorSystem].provider.getExternalAddressFor(Address("akka", "s", Some("host"), Some(1))).get + + override def atTermination() { other.shutdown() } + + "demonstrate programmatic deployment" in { + //#deploy + val ref = system.actorOf(Props[Echo].withDeploy(Deploy(scope = RemoteScope(address)))) + //#deploy + ref.path.address must be(address) + ref ! "test" + expectMsgType[ActorRef].path.address must be(address) + } + + "demonstrate address extractor" in { + //#make-address + val one = AddressExtractor("akka://sys@host:1234") + val two = Address("akka", "sys", "host", 1234) // this gives the same + //#make-address + one must be === two + } + +} \ No newline at end of file diff --git a/akka-docs/scala/remoting.rst b/akka-docs/scala/remoting.rst index 2d460aa060..2e1c4b7b8c 100644 --- a/akka-docs/scala/remoting.rst +++ b/akka-docs/scala/remoting.rst @@ -4,6 +4,11 @@ Remoting (Scala) ################# + +.. sidebar:: Contents + + .. contents:: :local: + For an introduction of remoting capabilities of Akka please see :ref:`remoting`. Preparing your ActorSystem for Remoting @@ -32,8 +37,12 @@ to your ``application.conf`` file:: As you can see in the example above there are four things you need to add to get started: * Change provider from ``akka.actor.LocalActorRefProvider`` to ``akka.remote.RemoteActorRefProvider`` -* Add host name - the machine you want to run the actor system on -* Add port number - the port the actor system should listen on +* Add host name - the machine you want to run the actor system on; this host + name is exactly what is passed to remote systems in order to identify this + system and consequently used for connecting back to this system if need be, + hence set it to a reachable IP address or resolvable name in case you want to + communicate across the network. +* Add port number - the port the actor system should listen on, set to 0 to have it chosen automatically The example above only illustrates the bare minimum of properties you have to add to enable remoting. There are lots of more properties that are related to remoting in Akka. We refer to the following @@ -63,7 +72,7 @@ As you can see from the example above the following pattern is used to find an ` akka://@:/ -Once you a reference to the actor you can interact with it they same way you would with a local actor, e.g.:: +Once you obtained a reference to the actor you can interact with it they same way you would with a local actor, e.g.:: actor ! "Pretty awesome feature" @@ -73,18 +82,19 @@ Creating Actors Remotely ^^^^^^^^^^^^^^^^^^^^^^^^ If you want to use the creation functionality in Akka remoting you have to further amend the -``application.conf`` file in the following way:: +``application.conf`` file in the following way (only showing deployment section):: akka { actor { - provider = "akka.remote.RemoteActorRefProvider" - deployment { /sampleActor { - remote = "akka://sampleActorSystem@127.0.0.1:2553" - }} + deployment { + /sampleActor { + remote = "akka://sampleActorSystem@127.0.0.1:2553" + } + } } - ... + } -The configuration above instructs Akka to react when an actor with path /sampleActor is created, i.e. +The configuration above instructs Akka to react when an actor with path ``/sampleActor`` is created, i.e. using ``system.actorOf(Props(...)`, sampleActor)``. This specific actor will not be directly instantiated, but instead the remote daemon of the remote system will be asked to create the actor, which in this sample corresponds to ``sampleActorSystem@127.0.0.1:2553``. @@ -99,12 +109,26 @@ Once you have configured the properties above you would do the following in code ``SampleActor`` has to be available to the runtimes using it, i.e. the classloader of the actor systems has to have a JAR containing the class. -Remote Sample Code -^^^^^^^^^^^^^^^^^^ +Programmatic Remote Deployment +------------------------------ -There is a more extensive remote example that comes with the Akka distribution. -Please have a look here for more information: -`Remote Sample `_ +To allow dynamically deployed systems, it is also possible to include +deployment configuration in the :class:`Props` which are used to create an +actor: this information is the equivalent of a deployment section from the +configuration file, and if both are given, the external configuration takes +precedence. + +With these imports: + +.. includecode:: code/akka/docs/remoting/RemoteDeploymentDocSpec.scala#import + +and a remote address like this: + +.. includecode:: code/akka/docs/remoting/RemoteDeploymentDocSpec.scala#make-address + +you can advise the system to create a child on that remote node like so: + +.. includecode:: code/akka/docs/remoting/RemoteDeploymentDocSpec.scala#deploy Serialization ^^^^^^^^^^^^^ @@ -140,9 +164,12 @@ the two given target nodes. Description of the Remoting Sample ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -The sample application included with the Akka sources demonstrates both, remote -deployment and look-up of remote actors. First, let us have a look at the -common setup for both scenarios (this is ``common.conf``): +There is a more extensive remote example that comes with the Akka distribution. +Please have a look here for more information: `Remote Sample +`_ +This sample demonstrates both, remote deployment and look-up of remote actors. +First, let us have a look at the common setup for both scenarios (this is +``common.conf``): .. includecode:: ../../akka-samples/akka-sample-remote/src/main/resources/common.conf diff --git a/akka-remote/src/main/scala/akka/remote/RemoteActorRefProvider.scala b/akka-remote/src/main/scala/akka/remote/RemoteActorRefProvider.scala index 9df72fd1a9..c49c529572 100644 --- a/akka-remote/src/main/scala/akka/remote/RemoteActorRefProvider.scala +++ b/akka-remote/src/main/scala/akka/remote/RemoteActorRefProvider.scala @@ -206,6 +206,16 @@ class RemoteActorRefProvider( // we don’t wait for the ACK, because the remote end will process this command before any other message to the new actor actorFor(RootActorPath(path.address) / "remote") ! DaemonMsgCreate(props, deploy, path.toString, supervisor) } + + def getExternalAddressFor(addr: Address): Option[Address] = { + val ta = transport.address + val ra = rootPath.address + addr match { + case `ta` | `ra` ⇒ Some(rootPath.address) + case Address("akka", _, Some(_), Some(_)) ⇒ Some(transport.address) + case _ ⇒ None + } + } } trait RemoteRef extends ActorRefScope {