DOC: Routing (Java). See #1600
This commit is contained in:
parent
a8c6a0d891
commit
ed2b65c402
13 changed files with 670 additions and 76 deletions
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -0,0 +1,5 @@
|
|||
package akka.docs.jrouting;
|
||||
|
||||
import org.scalatest.junit.JUnitSuite
|
||||
|
||||
class CustomRouterDocTest extends CustomRouterDocTestBase with JUnitSuite
|
||||
|
|
@ -0,0 +1,144 @@
|
|||
/**
|
||||
* Copyright (C) 2009-2012 Typesafe Inc. <http://www.typesafe.com>
|
||||
*/
|
||||
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<Object> democratsResult = routedActor.ask(DemocratCountResult, timeout);
|
||||
Future<Object> 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<ActorRef> routees = Arrays.asList(new ActorRef[] { democratActor, republicanActor });
|
||||
|
||||
//#crRegisterRoutees
|
||||
registerRoutees(context, routees);
|
||||
//#crRegisterRoutees
|
||||
|
||||
//#crRoutingLogic
|
||||
return new CustomRoute() {
|
||||
@Override
|
||||
public Iterable<Destination> 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
|
||||
|
||||
}
|
||||
48
akka-docs/java/code/akka/docs/jrouting/FibonacciActor.java
Normal file
48
akka-docs/java/code/akka/docs/jrouting/FibonacciActor.java
Normal file
|
|
@ -0,0 +1,48 @@
|
|||
/**
|
||||
* Copyright (C) 2009-2012 Typesafe Inc. <http://www.typesafe.com>
|
||||
*/
|
||||
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
|
||||
|
||||
60
akka-docs/java/code/akka/docs/jrouting/ParentActor.java
Normal file
60
akka-docs/java/code/akka/docs/jrouting/ParentActor.java
Normal file
|
|
@ -0,0 +1,60 @@
|
|||
/**
|
||||
* Copyright (C) 2009-2012 Typesafe Inc. <http://www.typesafe.com>
|
||||
*/
|
||||
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<Object> 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
|
||||
15
akka-docs/java/code/akka/docs/jrouting/PrintlnActor.java
Normal file
15
akka-docs/java/code/akka/docs/jrouting/PrintlnActor.java
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
/**
|
||||
* Copyright (C) 2009-2012 Typesafe Inc. <http://www.typesafe.com>
|
||||
*/
|
||||
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
|
||||
|
|
@ -0,0 +1,51 @@
|
|||
/**
|
||||
* Copyright (C) 2009-2012 Typesafe Inc. <http://www.typesafe.com>
|
||||
*/
|
||||
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));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,60 @@
|
|||
/**
|
||||
* Copyright (C) 2009-2012 Typesafe Inc. <http://www.typesafe.com>
|
||||
*/
|
||||
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<ActorRef> 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));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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 <http://en.wikipedia.org/wiki/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 <https://github.com/jboner/akka/blob/master/akka-actor/src/main/scala/akka/routing/Routing.scala#L375>`_
|
||||
|
||||
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 <http://akka.io/docs/akka/snapshot/>`_.
|
||||
|
||||
You can also get some ideas of the routing by looking at the corresponding :ref:`routing-scala` documentation.
|
||||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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) }
|
||||
}
|
||||
|
|
@ -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 <https://github.com/jboner/akka/blob/master/akka-actor/src/main/scala/akka/routing/Routing.scala#L173>`_
|
||||
* `RandomRouter <https://github.com/jboner/akka/blob/master/akka-actor/src/main/scala/akka/routing/Routing.scala#L226>`_
|
||||
* `BroadcastRouter <https://github.com/jboner/akka/blob/master/akka-actor/src/main/scala/akka/routing/Routing.scala#L284>`_
|
||||
* `ScatterGatherFirstCompletedRouter <https://github.com/jboner/akka/blob/master/akka-actor/src/main/scala/akka/routing/Routing.scala#L330>`_
|
||||
* ``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 <https://github.com/jboner/akka/blob/master/akka-actor/src/main/scala/akka/routing/Routing.scala#L330>`_
|
||||
`ScatterGatherFirstCompletedRouter <https://github.com/jboner/akka/blob/master/akka-actor/src/main/scala/akka/routing/Routing.scala#L375>`_
|
||||
|
||||
All in all the custom router looks like this:
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue