diff --git a/akka-actor-tests/src/test/scala/akka/actor/DeployerSpec.scala b/akka-actor-tests/src/test/scala/akka/actor/DeployerSpec.scala index 6c0e699800..92e1f4f3ab 100644 --- a/akka-actor-tests/src/test/scala/akka/actor/DeployerSpec.scala +++ b/akka-actor-tests/src/test/scala/akka/actor/DeployerSpec.scala @@ -13,28 +13,28 @@ import akka.util.duration._ object DeployerSpec { val deployerConf = ConfigFactory.parseString(""" akka.actor.deployment { - /user/service1 { + /service1 { } - /user/service3 { + /service3 { create-as { class = "akka.actor.DeployerSpec$RecipeActor" } } - /user/service-direct { + /service-direct { router = from-code } - /user/service-direct2 { + /service-direct2 { router = from-code # nr-of-instances ignored when router = direct nr-of-instances = 2 } - /user/service-round-robin { + /service-round-robin { router = round-robin } - /user/service-random { + /service-random { router = random } - /user/service-scatter-gather { + /service-scatter-gather { router = scatter-gather within = 2 seconds } @@ -53,7 +53,7 @@ class DeployerSpec extends AkkaSpec(DeployerSpec.deployerConf) { "A Deployer" must { "be able to parse 'akka.actor.deployment._' with all default values" in { - val service = "/user/service1" + val service = "/service1" val deployment = system.asInstanceOf[ActorSystemImpl].provider.deployer.lookup(service) deployment must be('defined) @@ -67,13 +67,13 @@ class DeployerSpec extends AkkaSpec(DeployerSpec.deployerConf) { } "use None deployment for undefined service" in { - val service = "/user/undefined" + val service = "/undefined" val deployment = system.asInstanceOf[ActorSystemImpl].provider.deployer.lookup(service) deployment must be(None) } "be able to parse 'akka.actor.deployment._' with recipe" in { - val service = "/user/service3" + val service = "/service3" val deployment = system.asInstanceOf[ActorSystemImpl].provider.deployer.lookup(service) deployment must be('defined) @@ -90,7 +90,7 @@ class DeployerSpec extends AkkaSpec(DeployerSpec.deployerConf) { intercept[com.typesafe.config.ConfigException.WrongType] { val invalidDeployerConf = ConfigFactory.parseString(""" akka.actor.deployment { - /user/service-invalid-number-of-instances { + /service-invalid-number-of-instances { router = round-robin nr-of-instances = boom } @@ -102,23 +102,23 @@ class DeployerSpec extends AkkaSpec(DeployerSpec.deployerConf) { } "be able to parse 'akka.actor.deployment._' with direct router" in { - assertRouting(NoRouter, "/user/service-direct") + assertRouting(NoRouter, "/service-direct") } "ignore nr-of-instances with direct router" in { - assertRouting(NoRouter, "/user/service-direct2") + assertRouting(NoRouter, "/service-direct2") } "be able to parse 'akka.actor.deployment._' with round-robin router" in { - assertRouting(RoundRobinRouter(1), "/user/service-round-robin") + assertRouting(RoundRobinRouter(1), "/service-round-robin") } "be able to parse 'akka.actor.deployment._' with random router" in { - assertRouting(RandomRouter(1), "/user/service-random") + assertRouting(RandomRouter(1), "/service-random") } "be able to parse 'akka.actor.deployment._' with scatter-gather router" in { - assertRouting(ScatterGatherFirstCompletedRouter(nrOfInstances = 1, within = 2 seconds), "/user/service-scatter-gather") + assertRouting(ScatterGatherFirstCompletedRouter(nrOfInstances = 1, within = 2 seconds), "/service-scatter-gather") } def assertRouting(expected: RouterConfig, service: String) { diff --git a/akka-actor-tests/src/test/scala/akka/routing/RoutingSpec.scala b/akka-actor-tests/src/test/scala/akka/routing/RoutingSpec.scala index fb2aa50372..711bf04371 100644 --- a/akka-actor-tests/src/test/scala/akka/routing/RoutingSpec.scala +++ b/akka-actor-tests/src/test/scala/akka/routing/RoutingSpec.scala @@ -5,7 +5,7 @@ package akka.routing import java.util.concurrent.atomic.AtomicInteger import akka.actor._ -import collection.mutable.LinkedList +import scala.collection.mutable.LinkedList import akka.testkit._ import akka.util.duration._ import akka.dispatch.Await @@ -33,8 +33,6 @@ object RoutingSpec { @org.junit.runner.RunWith(classOf[org.scalatest.junit.JUnitRunner]) class RoutingSpec extends AkkaSpec with DefaultTimeout with ImplicitSender { - val impl = system.asInstanceOf[ActorSystemImpl] - import akka.routing.RoutingSpec._ "routers in general" must { @@ -399,7 +397,7 @@ class RoutingSpec extends AkkaSpec with DefaultTimeout with ImplicitSender { } "count votes as intended - not as in Florida" in { - val routedActor = system.actorOf(Props[TestActor].withRouter(VoteCountRouter)) + val routedActor = system.actorOf(Props().withRouter(VoteCountRouter)) routedActor ! DemocratVote routedActor ! DemocratVote routedActor ! RepublicanVote diff --git a/akka-docs/java/code/akka/docs/jrouting/CustomRouterDocTest.scala b/akka-docs/java/code/akka/docs/jrouting/CustomRouterDocTest.scala new file mode 100644 index 0000000000..48e323c634 --- /dev/null +++ b/akka-docs/java/code/akka/docs/jrouting/CustomRouterDocTest.scala @@ -0,0 +1,5 @@ +package akka.docs.jrouting; + +import org.scalatest.junit.JUnitSuite + +class CustomRouterDocTest extends CustomRouterDocTestBase with JUnitSuite diff --git a/akka-docs/java/code/akka/docs/jrouting/CustomRouterDocTestBase.java b/akka-docs/java/code/akka/docs/jrouting/CustomRouterDocTestBase.java new file mode 100644 index 0000000000..8962b22c57 --- /dev/null +++ b/akka-docs/java/code/akka/docs/jrouting/CustomRouterDocTestBase.java @@ -0,0 +1,144 @@ +/** + * Copyright (C) 2009-2012 Typesafe Inc. + */ +package akka.docs.jrouting; + +import java.util.List; +import java.util.Arrays; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import static org.junit.Assert.assertEquals; + +import akka.actor.*; +import akka.routing.*; +import akka.util.Duration; +import akka.util.Timeout; +import akka.dispatch.Await; +import akka.dispatch.Future; +import akka.testkit.AkkaSpec; +import com.typesafe.config.ConfigFactory; + +import static akka.docs.jrouting.CustomRouterDocTestBase.DemocratActor; +import static akka.docs.jrouting.CustomRouterDocTestBase.RepublicanActor; +import static akka.docs.jrouting.CustomRouterDocTestBase.Message.*; + +public class CustomRouterDocTestBase { + + ActorSystem system; + + @Before + public void setUp() { + system = ActorSystem.create("MySystem", AkkaSpec.testConf()); + } + + @After + public void tearDown() { + system.shutdown(); + } + + //#crTest + @Test + public void countVotesAsIntendedNotAsInFlorida() { + ActorRef routedActor = system.actorOf(new Props().withRouter(new VoteCountRouter())); + routedActor.tell(DemocratVote); + routedActor.tell(DemocratVote); + routedActor.tell(RepublicanVote); + routedActor.tell(DemocratVote); + routedActor.tell(RepublicanVote); + Timeout timeout = new Timeout(Duration.parse("1 seconds")); + Future democratsResult = routedActor.ask(DemocratCountResult, timeout); + Future republicansResult = routedActor.ask(RepublicanCountResult, timeout); + + assertEquals(3, Await.result(democratsResult, timeout.duration())); + assertEquals(2, Await.result(republicansResult, timeout.duration())); + } + + //#crTest + + //#CustomRouter + //#crMessages + enum Message { + DemocratVote, DemocratCountResult, RepublicanVote, RepublicanCountResult + } + + //#crMessages + + //#crActors + public static class DemocratActor extends UntypedActor { + int counter = 0; + + public void onReceive(Object msg) { + switch ((Message) msg) { + case DemocratVote: + counter++; + break; + case DemocratCountResult: + getSender().tell(counter, getSelf()); + break; + default: + unhandled(msg); + } + } + } + + public static class RepublicanActor extends UntypedActor { + int counter = 0; + + public void onReceive(Object msg) { + switch ((Message) msg) { + case RepublicanVote: + counter++; + break; + case RepublicanCountResult: + getSender().tell(counter, getSelf()); + break; + default: + unhandled(msg); + } + } + } + + //#crActors + + //#crRouter + public static class VoteCountRouter extends CustomRouterConfig { + + //#crRoute + @Override + public CustomRoute createCustomRoute(Props props, ActorContext context, RoutedActorRef ref) { + final ActorRef democratActor = context.actorOf(new Props(DemocratActor.class), "d"); + final ActorRef republicanActor = context.actorOf(new Props(RepublicanActor.class), "r"); + List routees = Arrays.asList(new ActorRef[] { democratActor, republicanActor }); + + //#crRegisterRoutees + registerRoutees(context, routees); + //#crRegisterRoutees + + //#crRoutingLogic + return new CustomRoute() { + @Override + public Iterable destinationsFor(ActorRef sender, Object msg) { + switch ((Message) msg) { + case DemocratVote: + case DemocratCountResult: + return Arrays.asList(new Destination[] { new Destination(sender, democratActor) }); + case RepublicanVote: + case RepublicanCountResult: + return Arrays.asList(new Destination[] { new Destination(sender, republicanActor) }); + default: + throw new IllegalArgumentException("Unknown message: " + msg); + } + } + }; + //#crRoutingLogic + } + //#crRoute + + } + + //#crRouter + //#CustomRouter + +} diff --git a/akka-docs/java/code/akka/docs/jrouting/FibonacciActor.java b/akka-docs/java/code/akka/docs/jrouting/FibonacciActor.java new file mode 100644 index 0000000000..8e426cf8fe --- /dev/null +++ b/akka-docs/java/code/akka/docs/jrouting/FibonacciActor.java @@ -0,0 +1,48 @@ +/** + * Copyright (C) 2009-2012 Typesafe Inc. + */ +package akka.docs.jrouting; + +import java.io.Serializable; + +import akka.actor.UntypedActor; + +//#fibonacciActor +public class FibonacciActor extends UntypedActor { + public void onReceive(Object msg) { + if (msg instanceof FibonacciNumber) { + FibonacciNumber fibonacciNumber = (FibonacciNumber) msg; + getSender().tell(fibonacci(fibonacciNumber.getNbr())); + } else { + unhandled(msg); + } + } + + private int fibonacci(int n) { + return fib(n, 1, 0); + } + + private int fib(int n, int b, int a) { + if (n == 0) + return a; + // recursion + return fib(n - 1, a + b, b); + } + + public static class FibonacciNumber implements Serializable { + private static final long serialVersionUID = 1L; + private final int nbr; + + public FibonacciNumber(int nbr) { + this.nbr = nbr; + } + + public int getNbr() { + return nbr; + } + + } +} + +//#fibonacciActor + diff --git a/akka-docs/java/code/akka/docs/jrouting/ParentActor.java b/akka-docs/java/code/akka/docs/jrouting/ParentActor.java new file mode 100644 index 0000000000..c8d8b019bb --- /dev/null +++ b/akka-docs/java/code/akka/docs/jrouting/ParentActor.java @@ -0,0 +1,60 @@ +/** + * Copyright (C) 2009-2012 Typesafe Inc. + */ +package akka.docs.jrouting; + +import akka.routing.ScatterGatherFirstCompletedRouter; +import akka.routing.BroadcastRouter; +import akka.routing.RandomRouter; +import akka.routing.RoundRobinRouter; +import akka.actor.UntypedActor; +import akka.actor.ActorRef; +import akka.actor.Props; +import akka.util.Duration; +import akka.util.Timeout; +import akka.dispatch.Future; +import akka.dispatch.Await; + +//#parentActor +public class ParentActor extends UntypedActor { + public void onReceive(Object msg) { + if (msg.equals("rrr")) { + //#roundRobinRouter + ActorRef roundRobinRouter = getContext().actorOf( + new Props(PrintlnActor.class).withRouter(new RoundRobinRouter(5)), "router"); + for (int i = 1; i <= 10; i++) { + roundRobinRouter.tell(i, getSelf()); + } + //#roundRobinRouter + } else if (msg.equals("rr")) { + //#randomRouter + ActorRef randomRouter = getContext().actorOf(new Props(PrintlnActor.class).withRouter(new RandomRouter(5)), + "router"); + for (int i = 1; i <= 10; i++) { + randomRouter.tell(i, getSelf()); + } + //#randomRouter + } else if (msg.equals("br")) { + //#broadcastRouter + ActorRef broadcastRouter = getContext().actorOf(new Props(PrintlnActor.class).withRouter(new BroadcastRouter(5)), + "router"); + broadcastRouter.tell("this is a broadcast message", getSelf()); + //#broadcastRouter + } else if (msg.equals("sgfcr")) { + //#scatterGatherFirstCompletedRouter + ActorRef scatterGatherFirstCompletedRouter = getContext().actorOf( + new Props(FibonacciActor.class).withRouter(new ScatterGatherFirstCompletedRouter(5, Duration + .parse("2 seconds"))), "router"); + Timeout timeout = getContext().system().settings().ActorTimeout(); + Future futureResult = scatterGatherFirstCompletedRouter.ask(new FibonacciActor.FibonacciNumber(10), + timeout); + int result = (Integer) Await.result(futureResult, timeout.duration()); + //#scatterGatherFirstCompletedRouter + System.out.println(String.format("The result of calculating Fibonacci for 10 is %d", result)); + } else { + unhandled(msg); + } + } +} + +//#parentActor \ No newline at end of file diff --git a/akka-docs/java/code/akka/docs/jrouting/PrintlnActor.java b/akka-docs/java/code/akka/docs/jrouting/PrintlnActor.java new file mode 100644 index 0000000000..d6ad652ebe --- /dev/null +++ b/akka-docs/java/code/akka/docs/jrouting/PrintlnActor.java @@ -0,0 +1,15 @@ +/** + * Copyright (C) 2009-2012 Typesafe Inc. + */ +package akka.docs.jrouting; + +import akka.actor.UntypedActor; + +//#printlnActor +public class PrintlnActor extends UntypedActor { + public void onReceive(Object msg) { + System.out.println(String.format("Received message '%s' in actor %s", msg, getSelf().path().name())); + } +} + +//#printlnActor diff --git a/akka-docs/java/code/akka/docs/jrouting/RouterViaConfigExample.java b/akka-docs/java/code/akka/docs/jrouting/RouterViaConfigExample.java new file mode 100644 index 0000000000..c33a22667b --- /dev/null +++ b/akka-docs/java/code/akka/docs/jrouting/RouterViaConfigExample.java @@ -0,0 +1,51 @@ +/** + * Copyright (C) 2009-2012 Typesafe Inc. + */ +package akka.docs.jrouting; + +import akka.routing.FromConfig; +import akka.actor.ActorRef; +import akka.actor.Props; +import akka.actor.UntypedActor; +import akka.actor.ActorSystem; +import com.typesafe.config.ConfigFactory; +import com.typesafe.config.Config; + +public class RouterViaConfigExample { + + public static class ExampleActor extends UntypedActor { + public void onReceive(Object msg) { + if (msg instanceof Message) { + Message message = (Message) msg; + System.out.println(String.format("Received %s in router %s", message.getNbr(), getSelf().path().name())); + } else { + unhandled(msg); + } + } + + public static class Message { + private final int nbr; + + public Message(int nbr) { + this.nbr = nbr; + } + + public int getNbr() { + return nbr; + } + + } + } + + public static void main(String... args) { + Config config = ConfigFactory.parseString("akka.actor.deployment {\n" + " /router {\n" + + " router = round-robin\n" + " nr-of-instances = 5\n" + " }\n" + "}\n"); + ActorSystem system = ActorSystem.create("Example", config); + //#configurableRouting + ActorRef router = system.actorOf(new Props(ExampleActor.class).withRouter(new FromConfig()), "router"); + //#configurableRouting + for (int i = 1; i <= 10; i++) { + router.tell(new ExampleActor.Message(i)); + } + } +} \ No newline at end of file diff --git a/akka-docs/java/code/akka/docs/jrouting/RouterViaProgramExample.java b/akka-docs/java/code/akka/docs/jrouting/RouterViaProgramExample.java new file mode 100644 index 0000000000..094ac8361f --- /dev/null +++ b/akka-docs/java/code/akka/docs/jrouting/RouterViaProgramExample.java @@ -0,0 +1,60 @@ +/** + * Copyright (C) 2009-2012 Typesafe Inc. + */ +package akka.docs.jrouting; + +import akka.routing.RoundRobinRouter; +import akka.actor.ActorRef; +import akka.actor.Props; +import akka.actor.UntypedActor; +import akka.actor.ActorSystem; +import java.util.Arrays; + +public class RouterViaProgramExample { + + public static class ExampleActor extends UntypedActor { + public void onReceive(Object msg) { + if (msg instanceof Message) { + Message message = (Message) msg; + System.out.println(String.format("Received %s in router %s", message.getNbr(), getSelf().path().name())); + } else { + unhandled(msg); + } + } + + public static class Message { + private final int nbr; + + public Message(int nbr) { + this.nbr = nbr; + } + + public int getNbr() { + return nbr; + } + + } + } + + public static void main(String... args) { + ActorSystem system = ActorSystem.create("RPE"); + //#programmaticRoutingNrOfInstances + int nrOfInstances = 5; + ActorRef router1 = system.actorOf(new Props(ExampleActor.class).withRouter(new RoundRobinRouter(nrOfInstances))); + //#programmaticRoutingNrOfInstances + for (int i = 1; i <= 6; i++) { + router1.tell(new ExampleActor.Message(i)); + } + + //#programmaticRoutingRoutees + ActorRef actor1 = system.actorOf(new Props(ExampleActor.class)); + ActorRef actor2 = system.actorOf(new Props(ExampleActor.class)); + ActorRef actor3 = system.actorOf(new Props(ExampleActor.class)); + Iterable routees = Arrays.asList(new ActorRef[] { actor1, actor2, actor3 }); + ActorRef router2 = system.actorOf(new Props(ExampleActor.class).withRouter(RoundRobinRouter.create(routees))); + //#programmaticRoutingRoutees + for (int i = 1; i <= 6; i++) { + router2.tell(new ExampleActor.Message(i)); + } + } +} \ No newline at end of file diff --git a/akka-docs/java/routing.rst b/akka-docs/java/routing.rst index 2c0f74c696..16c2a0864d 100644 --- a/akka-docs/java/routing.rst +++ b/akka-docs/java/routing.rst @@ -4,9 +4,218 @@ Routing (Java) ============== -This part of the documentation is not done. +.. sidebar:: Contents + + .. contents:: :local: + +Akka-core includes some building blocks to build more complex message flow handlers, they are listed and explained below: + +Router +------ + +A Router is an actor that routes incoming messages to outbound actors. +The router routes the messages sent to it to its underlying actors called 'routees'. + +Akka comes with four defined routers out of the box, but as you will see in this chapter it +is really easy to create your own. The four routers shipped with Akka are: + +* ``akka.routing.RoundRobinRouter`` +* ``akka.routing.RandomRouter`` +* ``akka.routing.BroadcastRouter`` +* ``akka.routing.ScatterGatherFirstCompletedRouter`` + +Routers Explained +^^^^^^^^^^^^^^^^^ + +This is an example of how to create a router that is defined in configuration: + +.. includecode:: ../scala/code/akka/docs/routing/RouterViaConfigExample.scala#config + +.. includecode:: code/akka/docs/jrouting/RouterViaConfigExample.java#configurableRouting + +This is an example of how to programatically create a router and set the number of routees it should create: + +.. includecode:: code/akka/docs/jrouting/RouterViaProgramExample.java#programmaticRoutingNrOfInstances + +You can also give the router already created routees as in: + +.. includecode:: code/akka/docs/jrouting/RouterViaProgramExample.java#programmaticRoutingRoutees + +When you create a router programatically you define the number of routees *or* you pass already created routees to it. +If you send both parameters to the router *only* the latter will be used, i.e. ``nrOfInstances`` is disregarded. + +*It is also worth pointing out that if you define the number of routees in the configuration file then this +value will be used instead of any programmatically sent parameters.* + +Once you have the router actor it is just to send messages to it as you would to any actor: + +.. code-block:: java + + router.tell(new MyMsg()); + +The router will apply its behavior to the message it receives and forward it to the routees. + +Router usage +^^^^^^^^^^^^ + +In this section we will describe how to use the different router types. +First we need to create some actors that will be used in the examples: + +.. includecode:: code/akka/docs/jrouting/PrintlnActor.java#printlnActor + +and + +.. includecode:: code/akka/docs/jrouting/FibonacciActor.java#fibonacciActor + +RoundRobinRouter +**************** +Routes in a `round-robin `_ fashion to its routees. +Code example: + +.. includecode:: code/akka/docs/jrouting/ParentActor.java#roundRobinRouter + +When run you should see a similar output to this: + +.. code-block:: scala + + Received message '1' in actor $b + Received message '2' in actor $c + Received message '3' in actor $d + Received message '6' in actor $b + Received message '4' in actor $e + Received message '8' in actor $d + Received message '5' in actor $f + Received message '9' in actor $e + Received message '10' in actor $f + Received message '7' in actor $c + +If you look closely to the output you can see that each of the routees received two messages which +is exactly what you would expect from a round-robin router to happen. +(The name of an actor is automatically created in the format ``$letter`` unless you specify it - +hence the names printed above.) + +RandomRouter +************ +As the name implies this router type selects one of its routees randomly and forwards +the message it receives to this routee. +This procedure will happen each time it receives a message. +Code example: + +.. includecode:: code/akka/docs/jrouting/ParentActor.java#randomRouter + +When run you should see a similar output to this: + +.. code-block:: scala + + Received message '1' in actor $e + Received message '2' in actor $c + Received message '4' in actor $b + Received message '5' in actor $d + Received message '3' in actor $e + Received message '6' in actor $c + Received message '7' in actor $d + Received message '8' in actor $e + Received message '9' in actor $d + Received message '10' in actor $d + +The result from running the random router should be different, or at least random, every time you run it. +Try to run it a couple of times to verify its behavior if you don't trust us. + +BroadcastRouter +*************** +A broadcast router forwards the message it receives to *all* its routees. +Code example: + +.. includecode:: code/akka/docs/jrouting/ParentActor.java#broadcastRouter + +When run you should see a similar output to this: + +.. code-block:: scala + + Received message 'this is a broadcast message' in actor $f + Received message 'this is a broadcast message' in actor $d + Received message 'this is a broadcast message' in actor $e + Received message 'this is a broadcast message' in actor $c + Received message 'this is a broadcast message' in actor $b + +As you can see here above each of the routees, five in total, received the broadcast message. + +ScatterGatherFirstCompletedRouter +********************************* +The ScatterGatherFirstCompletedRouter will send the message on to all its routees as a future. +It then waits for first result it gets back. This result will be sent back to original sender. +Code example: + +.. includecode:: code/akka/docs/jrouting/ParentActor.java#scatterGatherFirstCompletedRouter + +When run you should see this: + +.. code-block:: scala + + The result of calculating Fibonacci for 10 is 55 + +From the output above you can't really see that all the routees performed the calculation, but they did! +The result you see is from the first routee that returned its calculation to the router. + +Broadcast Messages +^^^^^^^^^^^^^^^^^^ + +There is a special type of message that will be sent to all routees regardless of the router. +This message is called ``Broadcast`` and is used in the following manner: + +.. code-block:: java + + router.tell(new Broadcast("Watch out for Davy Jones' locker")); + +Only the actual message is forwarded to the routees, i.e. "Watch out for Davy Jones' locker" in the example above. +It is up to the routee implementation whether to handle the broadcast message or not. + +Custom Router +^^^^^^^^^^^^^ + +You can also create your own router should you not find any of the ones provided by Akka sufficient for your needs. +In order to roll your own router you have to fulfill certain criteria which are explained in this section. + +The router created in this example is a simple vote counter. It will route the votes to specific vote counter actors. +In this case we only have two parties the Republicans and the Democrats. We would like a router that forwards all +democrat related messages to the Democrat actor and all republican related messages to the Republican actor. + +We begin with defining the class: + +.. includecode:: code/akka/docs/jrouting/CustomRouterDocTestBase.java#crRouter + :exclude: crRoute + +The next step is to implement the ``createCustomRoute`` method in the class just defined: + +.. includecode:: code/akka/docs/jrouting/CustomRouterDocTestBase.java#crRoute + +As you can see above we start off by creating the routees and put them in a collection. + +Make sure that you don't miss to implement the line below as it is *really* important. +It registers the routees internally and failing to call this method will +cause a ``ActorInitializationException`` to be thrown when the router is used. +Therefore always make sure to do the following in your custom router: + +.. includecode:: code/akka/docs/jrouting/CustomRouterDocTestBase.java#crRegisterRoutees + +The routing logic is where your magic sauce is applied. In our example it inspects the message types +and forwards to the correct routee based on this: + +.. includecode:: code/akka/docs/jrouting/CustomRouterDocTestBase.java#crRoutingLogic + +As you can see above what's returned in the ``CustomRoute`` function, which defines the mapping +from incoming sender/message to a ``List`` of ``Destination(sender, routee)``. +The sender is what "parent" the routee should see - changing this could be useful if you for example want +another actor than the original sender to intermediate the result of the routee (if there is a result). +For more information about how to alter the original sender we refer to the source code of +`ScatterGatherFirstCompletedRouter `_ + +All in all the custom router looks like this: + +.. includecode:: code/akka/docs/jrouting/CustomRouterDocTestBase.java#CustomRouter + +If you are interested in how to use the VoteCountRouter it looks like this: + +.. includecode:: code/akka/docs/jrouting/CustomRouterDocTestBase.java#crTest -We continuously strive to add and improve the documentation so you may want to have a -look at the `snapshot repository `_. -You can also get some ideas of the routing by looking at the corresponding :ref:`routing-scala` documentation. \ No newline at end of file diff --git a/akka-docs/scala/code/akka/docs/routing/RouterTypeExample.scala b/akka-docs/scala/code/akka/docs/routing/RouterTypeExample.scala index 63338e8357..d688da6544 100644 --- a/akka-docs/scala/code/akka/docs/routing/RouterTypeExample.scala +++ b/akka-docs/scala/code/akka/docs/routing/RouterTypeExample.scala @@ -46,7 +46,7 @@ class ParentActor extends Actor { case "rrr" ⇒ //#roundRobinRouter val roundRobinRouter = - context.actorOf(Props[PrintlnActor].withRouter(RoundRobinRouter()), "router") + context.actorOf(Props[PrintlnActor].withRouter(RoundRobinRouter(5)), "router") 1 to 10 foreach { i ⇒ roundRobinRouter ! i } @@ -54,7 +54,7 @@ class ParentActor extends Actor { case "rr" ⇒ //#randomRouter val randomRouter = - context.actorOf(Props[PrintlnActor].withRouter(RandomRouter()), "router") + context.actorOf(Props[PrintlnActor].withRouter(RandomRouter(5)), "router") 1 to 10 foreach { i ⇒ randomRouter ! i } @@ -62,14 +62,14 @@ class ParentActor extends Actor { case "br" ⇒ //#broadcastRouter val broadcastRouter = - context.actorOf(Props[PrintlnActor].withRouter(BroadcastRouter()), "router") + context.actorOf(Props[PrintlnActor].withRouter(BroadcastRouter(5)), "router") broadcastRouter ! "this is a broadcast message" //#broadcastRouter case "sgfcr" ⇒ //#scatterGatherFirstCompletedRouter val scatterGatherFirstCompletedRouter = context.actorOf( - Props[FibonacciActor].withRouter(ScatterGatherFirstCompletedRouter(within = 2 seconds)), - "router") + Props[FibonacciActor].withRouter(ScatterGatherFirstCompletedRouter( + nrOfInstances = 5, within = 2 seconds)), "router") implicit val timeout = context.system.settings.ActorTimeout val futureResult = scatterGatherFirstCompletedRouter ? FibonacciNumber(10) val result = Await.result(futureResult, timeout.duration) diff --git a/akka-docs/scala/code/akka/docs/routing/RouterViaConfigExample.scala b/akka-docs/scala/code/akka/docs/routing/RouterViaConfigExample.scala index d0d5dd59c7..d3c3e848c2 100644 --- a/akka-docs/scala/code/akka/docs/routing/RouterViaConfigExample.scala +++ b/akka-docs/scala/code/akka/docs/routing/RouterViaConfigExample.scala @@ -4,7 +4,8 @@ package akka.docs.routing import akka.actor.{ Actor, Props, ActorSystem } -import akka.routing.RoundRobinRouter +import com.typesafe.config.ConfigFactory +import akka.routing.FromConfig case class Message(nbr: Int) @@ -15,10 +16,20 @@ class ExampleActor extends Actor { } object RouterWithConfigExample extends App { - val system = ActorSystem("Example") + val config = ConfigFactory.parseString(""" + //#config + akka.actor.deployment { + /router { + router = round-robin + nr-of-instances = 5 + } + } + //#config + """) + val system = ActorSystem("Example", config) //#configurableRouting - val router = system.actorOf(Props[PrintlnActor].withRouter(RoundRobinRouter()), - "exampleActor") + val router = system.actorOf(Props[ExampleActor].withRouter(FromConfig()), + "router") //#configurableRouting 1 to 10 foreach { i ⇒ router ! Message(i) } } \ No newline at end of file diff --git a/akka-docs/scala/routing.rst b/akka-docs/scala/routing.rst index 9adc9493f0..f68b2400a6 100644 --- a/akka-docs/scala/routing.rst +++ b/akka-docs/scala/routing.rst @@ -19,13 +19,41 @@ The router routes the messages sent to it to its underlying actors called 'route Akka comes with four defined routers out of the box, but as you will see in this chapter it is really easy to create your own. The four routers shipped with Akka are: -* `RoundRobinRouter `_ -* `RandomRouter `_ -* `BroadcastRouter `_ -* `ScatterGatherFirstCompletedRouter `_ +* ``akka.routing.RoundRobinRouter`` +* ``akka.routing.RandomRouter`` +* ``akka.routing.BroadcastRouter`` +* ``akka.routing.ScatterGatherFirstCompletedRouter`` -To illustrate how to use the routers we will create a couple of simple actors and then use them in the -different router types. +Routers Explained +^^^^^^^^^^^^^^^^^ + +This is an example of how to create a router that is defined in configuration: + +.. includecode:: code/akka/docs/routing/RouterViaConfigExample.scala#config + +.. includecode:: code/akka/docs/routing/RouterViaConfigExample.scala#configurableRouting + +This is an example of how to programatically create a router and set the number of routees it should create: + +.. includecode:: code/akka/docs/routing/RouterViaProgramExample.scala#programmaticRoutingNrOfInstances + +You can also give the router already created routees as in: + +.. includecode:: code/akka/docs/routing/RouterViaProgramExample.scala#programmaticRoutingRoutees + +When you create a router programatically you define the number of routees *or* you pass already created routees to it. +If you send both parameters to the router *only* the latter will be used, i.e. ``nrOfInstances`` is disregarded. + +*It is also worth pointing out that if you define the number of routees in the configuration file then this +value will be used instead of any programmatically sent parameters.* + +Once you have the router actor it is just to send messages to it as you would to any actor: + +.. code-block:: scala + + router ! MyMsg + +The router will apply its behavior to the message it receives and forward it to the routees. Router usage ^^^^^^^^^^^^ @@ -39,13 +67,6 @@ and .. includecode:: code/akka/docs/routing/RouterTypeExample.scala#fibonacciActor -Here is the configuration file to instruct the routers how many instances of routees to create:: - - akka.actor.deployment { - /router { - nr-of-instances = 5 - } - } RoundRobinRouter **************** @@ -137,34 +158,6 @@ When run you should see this: From the output above you can't really see that all the routees performed the calculation, but they did! The result you see is from the first routee that returned its calculation to the router. -Routers Explained -^^^^^^^^^^^^^^^^^ - -In the example usage above we showed you how to use routers configured with a configuration file but routers -can also be configured programatically. - -This is an example of how to create a router and set the number of routees it should create: - -.. includecode:: code/akka/docs/routing/RouterViaProgramExample.scala#programmaticRoutingNrOfInstances - -You can also give the router already created routees as in: - -.. includecode:: code/akka/docs/routing/RouterViaProgramExample.scala#programmaticRoutingRoutees - -When you create a router programatically you define the number of routees *or* you pass already created routees to it. -If you send both parameters to the router *only* the latter will be used, i.e. ``nrOfInstances`` is disregarded. - -*It is also worth pointing out that if you define the number of routees in the configuration file then this -value will be used instead of any programmatically sent parameters.* - -Once you have the router actor it is just to send messages to it as you would to any actor: - -.. code-block:: scala - - router ! MyMsg - -The router will apply its behavior to the message it receives and forward it to the routees. - Broadcast Messages ^^^^^^^^^^^^^^^^^^ @@ -193,7 +186,7 @@ We begin with defining the class: .. includecode:: ../../akka-actor-tests/src/test/scala/akka/routing/RoutingSpec.scala#crRouter :exclude: crRoute -The next step is to implement the 'createRoute' method in the class just defined: +The next step is to implement the ``createRoute`` method in the class just defined: .. includecode:: ../../akka-actor-tests/src/test/scala/akka/routing/RoutingSpec.scala#crRoute @@ -215,7 +208,7 @@ As you can see above what's returned in the partial function is a ``List`` of `` The sender is what "parent" the routee should see - changing this could be useful if you for example want another actor than the original sender to intermediate the result of the routee (if there is a result). For more information about how to alter the original sender we refer to the source code of -`ScatterGatherFirstCompletedRouter `_ +`ScatterGatherFirstCompletedRouter `_ All in all the custom router looks like this: