!act,rem,clu #3549 Simplify and enhance routers
* Separate routing logic, to be usable stand alone, e.g. in actors * Simplify RouterConfig, only a factory * Move reading of config from Deployer to the RouterConfig * Distiction between Pool and Group router types * Remove usage of actorFor, use ActorSelection * Management messages to add and remove routees * Simplify the internals of RoutedActorCell & co * Move resize specific code to separate RoutedActorCell subclass * Change resizer api to only return capacity change * Resizer only allowed together with Pool * Re-implement all routers, and keep old api during deprecation phase * Replace ClusterRouterConfig, deprecation * Rewrite documentation * Migration guide * Also includes related ticket: +act #3087 Create nicer Props factories for RouterConfig
This commit is contained in:
parent
81ca6fe8c8
commit
ebadd567b2
104 changed files with 9671 additions and 5006 deletions
|
|
@ -429,12 +429,12 @@ when a node becomes reachable again, after having been unreachable.
|
|||
|
||||
There are two distinct types of routers.
|
||||
|
||||
* **Router that lookup existing actors and use them as routees.** The routees can be shared between
|
||||
routers running on different nodes in the cluster. One example of a use case for this
|
||||
type of router is a service running on some backend nodes in the cluster and
|
||||
used by routers running on front-end nodes in the cluster.
|
||||
* **Group - router that sends messages to the specified path using actor selection**
|
||||
The routees can be shared between routers running on different nodes in the cluster.
|
||||
One example of a use case for this type of router is a service running on some backend
|
||||
nodes in the cluster and used by routers running on front-end nodes in the cluster.
|
||||
|
||||
* **Router that creates new routees as child actors and deploy them on remote nodes.**
|
||||
* **Pool - router that creates routees as child actors and deploys them on remote nodes.**
|
||||
Each router will have its own routee instances. For example, if you start a router
|
||||
on 3 nodes in a 10 nodes cluster you will have 30 routee actors in total if the router is
|
||||
configured to use one inctance per node. The routees created by the the different routers
|
||||
|
|
@ -442,11 +442,11 @@ There are two distinct types of routers.
|
|||
is a single master that coordinate jobs and delegates the actual work to routees running
|
||||
on other nodes in the cluster.
|
||||
|
||||
Router with Lookup of Routees
|
||||
-----------------------------
|
||||
Router with Group of Routees
|
||||
----------------------------
|
||||
|
||||
When using a router with routees looked up on the cluster member nodes, i.e. the routees
|
||||
are already running, the configuration for a router looks like this:
|
||||
When using a ``Group`` you must start the routee actors on the cluster member nodes.
|
||||
That is not done by the router. The configuration for a group looks like this:
|
||||
|
||||
.. includecode:: ../../../akka-samples/akka-sample-cluster/src/multi-jvm/scala/sample/cluster/stats/StatsSampleSpec.scala#router-lookup-config
|
||||
|
||||
|
|
@ -472,10 +472,11 @@ The same type of router could also have been defined in code:
|
|||
|
||||
See :ref:`cluster_configuration_java` section for further descriptions of the settings.
|
||||
|
||||
Router Example with Lookup of Routees
|
||||
-------------------------------------
|
||||
Router Example with Group of Routees
|
||||
------------------------------------
|
||||
|
||||
Let's take a look at how to use a cluster aware router with lookup of routees.
|
||||
Let's take a look at how to use a cluster aware router with a group of routees,
|
||||
i.e. router sending to the paths of the routees.
|
||||
|
||||
The example application provides a service to calculate statistics for a text.
|
||||
When some text is sent to the service it splits it into words, and delegates the task
|
||||
|
|
@ -533,10 +534,10 @@ service nodes and 1 client::
|
|||
mvn exec:java \
|
||||
-Dexec.mainClass="sample.cluster.stats.japi.StatsSampleMain"
|
||||
|
||||
Router with Remote Deployed Routees
|
||||
-----------------------------------
|
||||
Router with Pool of Remote Deployed Routees
|
||||
-------------------------------------------
|
||||
|
||||
When using a router with routees created and deployed on the cluster member nodes
|
||||
When using a ``Pool`` with routees created and deployed on the cluster member nodes
|
||||
the configuration for a router looks like this:
|
||||
|
||||
.. includecode:: ../../../akka-samples/akka-sample-cluster/src/multi-jvm/scala/sample/cluster/stats/StatsSampleSingleMasterSpec.scala#router-deploy-config
|
||||
|
|
@ -555,8 +556,8 @@ The same type of router could also have been defined in code:
|
|||
|
||||
See :ref:`cluster_configuration_java` section for further descriptions of the settings.
|
||||
|
||||
Router Example with Remote Deployed Routees
|
||||
-------------------------------------------
|
||||
Router Example with Pool of Remote Deployed Routees
|
||||
---------------------------------------------------
|
||||
|
||||
Let's take a look at how to use a cluster aware router on single master node that creates
|
||||
and deploys workers. To keep track of a single master we use the :ref:`cluster-singleton`
|
||||
|
|
@ -626,7 +627,7 @@ Download the native Sigar libraries from `Maven Central <http://repo1.maven.org/
|
|||
Adaptive Load Balancing
|
||||
-----------------------
|
||||
|
||||
The ``AdaptiveLoadBalancingRouter`` performs load balancing of messages to cluster nodes based on the cluster metrics data.
|
||||
The ``AdaptiveLoadBalancingPool`` / ``AdaptiveLoadBalancingGroup`` performs load balancing of messages to cluster nodes based on the cluster metrics data.
|
||||
It uses random selection of routees with probabilities derived from the remaining capacity of the corresponding node.
|
||||
It can be configured to use a specific MetricsSelector to produce the probabilities, a.k.a. weights:
|
||||
|
||||
|
|
|
|||
|
|
@ -119,8 +119,9 @@ public class ConsistentHashingRouterDocTest {
|
|||
}
|
||||
};
|
||||
|
||||
ActorRef cache = system.actorOf(Props.create(Cache.class).withRouter(
|
||||
new ConsistentHashingRouter(10).withHashMapper(hashMapper)),
|
||||
ActorRef cache = system.actorOf(
|
||||
new ConsistentHashingRouter(10).withHashMapper(hashMapper).props(
|
||||
Props.create(Cache.class)),
|
||||
"cache");
|
||||
|
||||
cache.tell(new ConsistentHashableEnvelope(
|
||||
|
|
|
|||
|
|
@ -3,201 +3,164 @@
|
|||
*/
|
||||
package docs.jrouting;
|
||||
|
||||
import static akka.pattern.Patterns.ask;
|
||||
import static docs.jrouting.CustomRouterDocTest.Message.DemocratCountResult;
|
||||
import static docs.jrouting.CustomRouterDocTest.Message.DemocratVote;
|
||||
import static docs.jrouting.CustomRouterDocTest.Message.RepublicanCountResult;
|
||||
import static docs.jrouting.CustomRouterDocTest.Message.RepublicanVote;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import akka.routing.FromConfig;
|
||||
import akka.routing.RoundRobinRoutingLogic;
|
||||
import akka.routing.Routee;
|
||||
import akka.routing.RoutingLogic;
|
||||
import akka.routing.SeveralRoutees;
|
||||
import akka.testkit.AkkaJUnitActorSystemResource;
|
||||
import org.junit.*;
|
||||
|
||||
import scala.concurrent.Await;
|
||||
import scala.concurrent.Future;
|
||||
import scala.concurrent.duration.Duration;
|
||||
import org.junit.ClassRule;
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
import com.typesafe.config.ConfigFactory;
|
||||
|
||||
import scala.collection.immutable.IndexedSeq;
|
||||
import static akka.japi.Util.immutableIndexedSeq;
|
||||
import docs.jrouting.RouterDocTest.Parent;
|
||||
import docs.jrouting.RouterDocTest.Workers;
|
||||
import docs.routing.CustomRouterDocSpec;
|
||||
import akka.testkit.JavaTestKit;
|
||||
import akka.actor.ActorRef;
|
||||
import akka.actor.ActorSystem;
|
||||
import akka.actor.OneForOneStrategy;
|
||||
import akka.actor.Props;
|
||||
import akka.actor.SupervisorStrategy;
|
||||
//#imports1
|
||||
import akka.actor.UntypedActor;
|
||||
import akka.dispatch.Dispatchers;
|
||||
import akka.routing.CustomRoute;
|
||||
import akka.routing.CustomRouterConfig;
|
||||
import akka.routing.Destination;
|
||||
import akka.routing.RoundRobinRouter;
|
||||
import akka.routing.RouteeProvider;
|
||||
import akka.testkit.AkkaSpec;
|
||||
import akka.util.Timeout;
|
||||
import com.typesafe.config.ConfigFactory;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
//#imports1
|
||||
|
||||
|
||||
public class CustomRouterDocTest {
|
||||
|
||||
@ClassRule
|
||||
public static AkkaJUnitActorSystemResource actorSystemResource =
|
||||
new AkkaJUnitActorSystemResource(
|
||||
"CustomRouterDocTest", ConfigFactory.load(ConfigFactory.parseString(
|
||||
"head{}\nworkers{}").withFallback(AkkaSpec.testConf())));
|
||||
new AkkaJUnitActorSystemResource("CustomRouterDocTest",
|
||||
ConfigFactory.parseString(CustomRouterDocSpec.jconfig()));
|
||||
|
||||
private final ActorSystem system = actorSystemResource.getSystem();
|
||||
|
||||
public static class MyActor extends UntypedActor {
|
||||
@Override public void onReceive(Object o) {}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void demonstrateDispatchers() {
|
||||
//#dispatchers
|
||||
final ActorRef router = system.actorOf(Props.create(MyActor.class)
|
||||
// “head” router will run on "head" dispatcher
|
||||
.withRouter(new RoundRobinRouter(5).withDispatcher("head"))
|
||||
// MyActor “workers” will run on "workers" dispatcher
|
||||
.withDispatcher("workers"));
|
||||
//#dispatchers
|
||||
}
|
||||
|
||||
@Test
|
||||
public void demonstrateSupervisor() {
|
||||
//#supervision
|
||||
final SupervisorStrategy strategy =
|
||||
new OneForOneStrategy(5, Duration.create("1 minute"),
|
||||
Collections.<Class<? extends Throwable>>singletonList(Exception.class));
|
||||
final ActorRef router = system.actorOf(Props.create(MyActor.class)
|
||||
.withRouter(new RoundRobinRouter(5).withSupervisorStrategy(strategy)));
|
||||
//#supervision
|
||||
}
|
||||
|
||||
//#crTest
|
||||
@Test
|
||||
public void countVotesAsIntendedNotAsInFlorida() throws Exception {
|
||||
ActorRef routedActor = system.actorOf(
|
||||
Props.empty().withRouter(new VoteCountRouter()));
|
||||
routedActor.tell(DemocratVote, ActorRef.noSender());
|
||||
routedActor.tell(DemocratVote, ActorRef.noSender());
|
||||
routedActor.tell(RepublicanVote, ActorRef.noSender());
|
||||
routedActor.tell(DemocratVote, ActorRef.noSender());
|
||||
routedActor.tell(RepublicanVote, ActorRef.noSender());
|
||||
Timeout timeout = new Timeout(Duration.create(1, "seconds"));
|
||||
Future<Object> democratsResult =
|
||||
ask(routedActor, DemocratCountResult, timeout);
|
||||
Future<Object> republicansResult =
|
||||
ask(routedActor, 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
|
||||
//#CustomRouter
|
||||
static
|
||||
//#CustomRouter
|
||||
//#crActors
|
||||
public 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//#crActors
|
||||
//#CustomRouter
|
||||
static
|
||||
//#CustomRouter
|
||||
//#crActors
|
||||
public 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
|
||||
//#CustomRouter
|
||||
static
|
||||
//#CustomRouter
|
||||
//#crRouter
|
||||
public class VoteCountRouter extends CustomRouterConfig {
|
||||
//#routing-logic
|
||||
public class RedundancyRoutingLogic implements RoutingLogic {
|
||||
private final int nbrCopies;
|
||||
|
||||
@Override public String routerDispatcher() {
|
||||
return Dispatchers.DefaultDispatcherId();
|
||||
public RedundancyRoutingLogic(int nbrCopies) {
|
||||
this.nbrCopies = nbrCopies;
|
||||
}
|
||||
RoundRobinRoutingLogic roundRobin = new RoundRobinRoutingLogic();
|
||||
|
||||
@Override public SupervisorStrategy supervisorStrategy() {
|
||||
return SupervisorStrategy.defaultStrategy();
|
||||
}
|
||||
|
||||
//#crRoute
|
||||
@Override
|
||||
public CustomRoute createCustomRoute(RouteeProvider routeeProvider) {
|
||||
final ActorRef democratActor =
|
||||
routeeProvider.context().actorOf(Props.create(DemocratActor.class), "d");
|
||||
final ActorRef republicanActor =
|
||||
routeeProvider.context().actorOf(Props.create(RepublicanActor.class), "r");
|
||||
List<ActorRef> routees =
|
||||
Arrays.asList(new ActorRef[] { democratActor, republicanActor });
|
||||
|
||||
//#crRegisterRoutees
|
||||
routeeProvider.registerRoutees(routees);
|
||||
//#crRegisterRoutees
|
||||
|
||||
//#crRoutingLogic
|
||||
return new CustomRoute() {
|
||||
@Override
|
||||
public scala.collection.immutable.Seq<Destination> destinationsFor(
|
||||
ActorRef sender, Object msg) {
|
||||
switch ((Message) msg) {
|
||||
case DemocratVote:
|
||||
case DemocratCountResult:
|
||||
return akka.japi.Util.immutableSingletonSeq(
|
||||
new Destination(sender, democratActor));
|
||||
case RepublicanVote:
|
||||
case RepublicanCountResult:
|
||||
return akka.japi.Util.immutableSingletonSeq(
|
||||
new Destination(sender, republicanActor));
|
||||
default:
|
||||
throw new IllegalArgumentException("Unknown message: " + msg);
|
||||
}
|
||||
}
|
||||
};
|
||||
//#crRoutingLogic
|
||||
public Routee select(Object message, IndexedSeq<Routee> routees) {
|
||||
List<Routee> targets = new ArrayList<Routee>();
|
||||
for (int i = 0; i < nbrCopies; i++) {
|
||||
targets.add(roundRobin.select(message, routees));
|
||||
}
|
||||
return new SeveralRoutees(targets);
|
||||
}
|
||||
//#crRoute
|
||||
}
|
||||
//#routing-logic
|
||||
|
||||
static
|
||||
//#unit-test-logic
|
||||
public final class TestRoutee implements Routee {
|
||||
public final int n;
|
||||
|
||||
public TestRoutee(int n) {
|
||||
this.n = n;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void send(Object message, ActorRef sender) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return n;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
return (obj instanceof TestRoutee) &&
|
||||
n == ((TestRoutee) obj).n;
|
||||
}
|
||||
}
|
||||
|
||||
//#unit-test-logic
|
||||
|
||||
static public class Storage extends UntypedActor {
|
||||
public void onReceive(Object msg) {
|
||||
getSender().tell(msg, getSelf());
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void unitTestRoutingLogic() {
|
||||
//#unit-test-logic
|
||||
RedundancyRoutingLogic logic = new RedundancyRoutingLogic(3);
|
||||
|
||||
List<Routee> routeeList = new ArrayList<Routee>();
|
||||
for (int n = 1; n <= 7; n++) {
|
||||
routeeList.add(new TestRoutee(n));
|
||||
}
|
||||
IndexedSeq<Routee> routees = immutableIndexedSeq(routeeList);
|
||||
|
||||
SeveralRoutees r1 = (SeveralRoutees) logic.select("msg", routees);
|
||||
assertEquals(r1.getRoutees().get(0), routeeList.get(0));
|
||||
assertEquals(r1.getRoutees().get(1), routeeList.get(1));
|
||||
assertEquals(r1.getRoutees().get(2), routeeList.get(2));
|
||||
|
||||
SeveralRoutees r2 = (SeveralRoutees) logic.select("msg", routees);
|
||||
assertEquals(r2.getRoutees().get(0), routeeList.get(3));
|
||||
assertEquals(r2.getRoutees().get(1), routeeList.get(4));
|
||||
assertEquals(r2.getRoutees().get(2), routeeList.get(5));
|
||||
|
||||
SeveralRoutees r3 = (SeveralRoutees) logic.select("msg", routees);
|
||||
assertEquals(r3.getRoutees().get(0), routeeList.get(6));
|
||||
assertEquals(r3.getRoutees().get(1), routeeList.get(0));
|
||||
assertEquals(r3.getRoutees().get(2), routeeList.get(1));
|
||||
|
||||
//#unit-test-logic
|
||||
}
|
||||
|
||||
@Test
|
||||
public void demonstrateUsageOfCustomRouter() {
|
||||
new JavaTestKit(system) {{
|
||||
//#usage-1
|
||||
for (int n = 1; n <= 10; n++) {
|
||||
system.actorOf(Props.create(Storage.class), "s" + n);
|
||||
}
|
||||
|
||||
List<String> paths = new ArrayList<String>();
|
||||
for (int n = 1; n <= 10; n++) {
|
||||
paths.add("/user/s" + n);
|
||||
}
|
||||
|
||||
ActorRef redundancy1 =
|
||||
system.actorOf(new RedundancyGroup(paths, 3).props(),
|
||||
"redundancy1");
|
||||
redundancy1.tell("important", getTestActor());
|
||||
//#usage-1
|
||||
|
||||
for (int i = 0; i < 3; i++) {
|
||||
expectMsgEquals("important");
|
||||
}
|
||||
|
||||
//#usage-2
|
||||
ActorRef redundancy2 = system.actorOf(FromConfig.getInstance().props(),
|
||||
"redundancy2");
|
||||
redundancy2.tell("very important", getTestActor());
|
||||
//#usage-2
|
||||
|
||||
for (int i = 0; i < 5; i++) {
|
||||
expectMsgEquals("very important");
|
||||
}
|
||||
}};
|
||||
}
|
||||
|
||||
//#crRouter
|
||||
//#CustomRouter
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,48 +0,0 @@
|
|||
/**
|
||||
* Copyright (C) 2009-2013 Typesafe Inc. <http://www.typesafe.com>
|
||||
*/
|
||||
package 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()), getSelf());
|
||||
} 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
|
||||
|
||||
|
|
@ -1,75 +0,0 @@
|
|||
/**
|
||||
* Copyright (C) 2009-2013 Typesafe Inc. <http://www.typesafe.com>
|
||||
*/
|
||||
package docs.jrouting;
|
||||
|
||||
import akka.routing.ScatterGatherFirstCompletedRouter;
|
||||
import akka.routing.BroadcastRouter;
|
||||
import akka.routing.RandomRouter;
|
||||
import akka.routing.RoundRobinRouter;
|
||||
import akka.routing.SmallestMailboxRouter;
|
||||
import akka.actor.UntypedActor;
|
||||
import akka.actor.ActorRef;
|
||||
import akka.actor.Props;
|
||||
import scala.concurrent.duration.Duration;
|
||||
import akka.util.Timeout;
|
||||
import scala.concurrent.Future;
|
||||
import scala.concurrent.Await;
|
||||
|
||||
//#parentActor
|
||||
public class ParentActor extends UntypedActor {
|
||||
public void onReceive(Object msg) throws Exception {
|
||||
if (msg.equals("rrr")) {
|
||||
//#roundRobinRouter
|
||||
ActorRef roundRobinRouter = getContext().actorOf(
|
||||
Props.create(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(
|
||||
Props.create(PrintlnActor.class).withRouter(new RandomRouter(5)),
|
||||
"router");
|
||||
for (int i = 1; i <= 10; i++) {
|
||||
randomRouter.tell(i, getSelf());
|
||||
}
|
||||
//#randomRouter
|
||||
} else if (msg.equals("smr")) {
|
||||
//#smallestMailboxRouter
|
||||
ActorRef smallestMailboxRouter = getContext().actorOf(
|
||||
Props.create(PrintlnActor.class).withRouter(new SmallestMailboxRouter(5)),
|
||||
"router");
|
||||
for (int i = 1; i <= 10; i++) {
|
||||
smallestMailboxRouter.tell(i, getSelf());
|
||||
}
|
||||
//#smallestMailboxRouter
|
||||
} else if (msg.equals("br")) {
|
||||
//#broadcastRouter
|
||||
ActorRef broadcastRouter = getContext().actorOf(
|
||||
Props.create(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(
|
||||
Props.create(FibonacciActor.class).withRouter(
|
||||
new ScatterGatherFirstCompletedRouter(5, Duration.create(2, "seconds"))),
|
||||
"router");
|
||||
Timeout timeout = new Timeout(Duration.create(5, "seconds"));
|
||||
Future<Object> futureResult = akka.pattern.Patterns.ask(
|
||||
scatterGatherFirstCompletedRouter, 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
|
||||
|
|
@ -1,16 +0,0 @@
|
|||
/**
|
||||
* Copyright (C) 2009-2013 Typesafe Inc. <http://www.typesafe.com>
|
||||
*/
|
||||
package 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
|
||||
58
akka-docs/rst/java/code/docs/jrouting/RedundancyGroup.java
Normal file
58
akka-docs/rst/java/code/docs/jrouting/RedundancyGroup.java
Normal file
|
|
@ -0,0 +1,58 @@
|
|||
/**
|
||||
* Copyright (C) 2009-2013 Typesafe Inc. <http://www.typesafe.com>
|
||||
*/
|
||||
package docs.jrouting;
|
||||
|
||||
//#group
|
||||
import java.util.List;
|
||||
|
||||
import scala.Option;
|
||||
import scala.collection.immutable.Iterable;
|
||||
import akka.actor.ActorContext;
|
||||
import akka.actor.ActorPath;
|
||||
import akka.actor.ActorSystem;
|
||||
import akka.actor.Props;
|
||||
import akka.dispatch.Dispatchers;
|
||||
import akka.routing.Group;
|
||||
import akka.routing.Routee;
|
||||
import akka.routing.Router;
|
||||
import akka.routing.RouterActor;
|
||||
import akka.routing.RouterConfig;
|
||||
import akka.routing.RoutingLogic;
|
||||
|
||||
import com.typesafe.config.Config;
|
||||
|
||||
import akka.routing.GroupBase;
|
||||
import static docs.jrouting.CustomRouterDocTest.RedundancyRoutingLogic;
|
||||
|
||||
public class RedundancyGroup extends GroupBase {
|
||||
private final List<String> paths;
|
||||
private final int nbrCopies;
|
||||
|
||||
public RedundancyGroup(List<String> paths, int nbrCopies) {
|
||||
this.paths = paths;
|
||||
this.nbrCopies = nbrCopies;
|
||||
}
|
||||
|
||||
public RedundancyGroup(Config config) {
|
||||
this(config.getStringList("routees.paths"),
|
||||
config.getInt("nbr-copies"));
|
||||
}
|
||||
|
||||
@Override
|
||||
public java.lang.Iterable<String> getPaths() {
|
||||
return paths;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Router createRouter(ActorSystem system) {
|
||||
return new Router(new RedundancyRoutingLogic(nbrCopies));
|
||||
}
|
||||
|
||||
@Override
|
||||
public String routerDispatcher() {
|
||||
return Dispatchers.DefaultDispatcherId();
|
||||
}
|
||||
|
||||
}
|
||||
//#group
|
||||
412
akka-docs/rst/java/code/docs/jrouting/RouterDocTest.java
Normal file
412
akka-docs/rst/java/code/docs/jrouting/RouterDocTest.java
Normal file
|
|
@ -0,0 +1,412 @@
|
|||
/**
|
||||
* Copyright (C) 2009-2013 Typesafe Inc. <http://www.typesafe.com>
|
||||
*/
|
||||
package docs.jrouting;
|
||||
|
||||
import akka.testkit.AkkaJUnitActorSystemResource;
|
||||
|
||||
import org.junit.ClassRule;
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
import scala.concurrent.duration.Duration;
|
||||
import scala.concurrent.duration.FiniteDuration;
|
||||
|
||||
import com.typesafe.config.ConfigFactory;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import akka.testkit.JavaTestKit;
|
||||
import akka.actor.ActorSystem;
|
||||
|
||||
|
||||
//#imports1
|
||||
import akka.actor.ActorRef;
|
||||
import akka.actor.Props;
|
||||
import akka.actor.Terminated;
|
||||
import akka.actor.UntypedActor;
|
||||
import akka.routing.ActorRefRoutee;
|
||||
import akka.routing.Routee;
|
||||
import akka.routing.Router;
|
||||
|
||||
//#imports1
|
||||
|
||||
|
||||
//#imports2
|
||||
import akka.actor.Address;
|
||||
import akka.actor.AddressFromURIString;
|
||||
import akka.actor.Kill;
|
||||
import akka.actor.PoisonPill;
|
||||
import akka.actor.SupervisorStrategy;
|
||||
import akka.actor.OneForOneStrategy;
|
||||
import akka.remote.routing.RemoteRouterConfig;
|
||||
import akka.routing.Broadcast;
|
||||
import akka.routing.BroadcastGroup;
|
||||
import akka.routing.BroadcastPool;
|
||||
import akka.routing.ConsistentHashingGroup;
|
||||
import akka.routing.ConsistentHashingPool;
|
||||
import akka.routing.DefaultResizer;
|
||||
import akka.routing.FromConfig;
|
||||
import akka.routing.RandomGroup;
|
||||
import akka.routing.RandomPool;
|
||||
import akka.routing.RoundRobinGroup;
|
||||
import akka.routing.RoundRobinPool;
|
||||
import akka.routing.RoundRobinRoutingLogic;
|
||||
import akka.routing.ScatterGatherFirstCompletedGroup;
|
||||
import akka.routing.ScatterGatherFirstCompletedPool;
|
||||
import akka.routing.SmallestMailboxPool;
|
||||
|
||||
//#imports2
|
||||
|
||||
public class RouterDocTest {
|
||||
|
||||
@ClassRule
|
||||
public static AkkaJUnitActorSystemResource actorSystemResource =
|
||||
new AkkaJUnitActorSystemResource("RouterDocTest",
|
||||
ConfigFactory.parseString(docs.routing.RouterDocSpec.config()));
|
||||
|
||||
private final ActorSystem system = actorSystemResource.getSystem();
|
||||
|
||||
|
||||
static
|
||||
//#router-in-actor
|
||||
public final class Work implements Serializable {
|
||||
private static final long serialVersionUID = 1L;
|
||||
public final String payload;
|
||||
public Work(String payload) {
|
||||
this.payload = payload;
|
||||
}
|
||||
}
|
||||
|
||||
//#router-in-actor
|
||||
static
|
||||
//#router-in-actor
|
||||
public class Master extends UntypedActor {
|
||||
|
||||
Router router;
|
||||
{
|
||||
List<Routee> routees = new ArrayList<Routee>();
|
||||
for (int i = 0; i < 5; i++) {
|
||||
ActorRef r = getContext().actorOf(Props.create(Worker.class));
|
||||
getContext().watch(r);
|
||||
routees.add(new ActorRefRoutee(r));
|
||||
}
|
||||
router = new Router(new RoundRobinRoutingLogic(), routees);
|
||||
}
|
||||
|
||||
public void onReceive(Object msg) {
|
||||
if (msg instanceof Work) {
|
||||
router.route(msg, getSender());
|
||||
} else if (msg instanceof Terminated) {
|
||||
router = router.removeRoutee(((Terminated) msg).actor());
|
||||
ActorRef r = getContext().actorOf(Props.create(Worker.class));
|
||||
getContext().watch(r);
|
||||
router = router.addRoutee(new ActorRefRoutee(r));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//#router-in-actor
|
||||
|
||||
static public class Worker extends UntypedActor {
|
||||
public void onReceive(Object msg) {}
|
||||
}
|
||||
|
||||
static public class Echo extends UntypedActor {
|
||||
public void onReceive(Object msg) {
|
||||
getSender().tell(msg, getSelf());
|
||||
}
|
||||
}
|
||||
|
||||
static public class Replier extends UntypedActor {
|
||||
public void onReceive(Object msg) {
|
||||
//#reply-with-self
|
||||
getSender().tell("reply", getSelf());
|
||||
//#reply-with-self
|
||||
|
||||
//#reply-with-parent
|
||||
getSender().tell("reply", getContext().parent());
|
||||
//#reply-with-parent
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
static
|
||||
//#create-worker-actors
|
||||
public class Workers extends UntypedActor {
|
||||
@Override public void preStart() {
|
||||
getContext().actorOf(Props.create(Worker.class), "w1");
|
||||
getContext().actorOf(Props.create(Worker.class), "w2");
|
||||
getContext().actorOf(Props.create(Worker.class), "w3");
|
||||
}
|
||||
// ...
|
||||
//#create-worker-actors
|
||||
|
||||
|
||||
public void onReceive(Object msg) {}
|
||||
}
|
||||
|
||||
static public class Parent extends UntypedActor {
|
||||
|
||||
//#paths
|
||||
List<String> paths = Arrays.asList("/user/workers/w1", "/user/workers/w2",
|
||||
"/user/workers/w3");
|
||||
//#paths
|
||||
|
||||
//#round-robin-pool-1
|
||||
ActorRef router1 =
|
||||
getContext().actorOf(FromConfig.getInstance().props(Props.create(Worker.class)),
|
||||
"router1");
|
||||
//#round-robin-pool-1
|
||||
|
||||
//#round-robin-pool-2
|
||||
ActorRef router2 =
|
||||
getContext().actorOf(new RoundRobinPool(5).props(Props.create(Worker.class)),
|
||||
"router2");
|
||||
//#round-robin-pool-2
|
||||
|
||||
//#round-robin-group-1
|
||||
ActorRef router3 =
|
||||
getContext().actorOf(FromConfig.getInstance().props(), "router3");
|
||||
//#round-robin-group-1
|
||||
|
||||
//#round-robin-group-2
|
||||
ActorRef router4 =
|
||||
getContext().actorOf(new RoundRobinGroup(paths).props(), "router4");
|
||||
//#round-robin-group-2
|
||||
|
||||
//#random-pool-1
|
||||
ActorRef router5 =
|
||||
getContext().actorOf(FromConfig.getInstance().props(
|
||||
Props.create(Worker.class)), "router5");
|
||||
//#random-pool-1
|
||||
|
||||
//#random-pool-2
|
||||
ActorRef router6 =
|
||||
getContext().actorOf(new RandomPool(5).props(Props.create(Worker.class)),
|
||||
"router6");
|
||||
//#random-pool-2
|
||||
|
||||
//#random-group-1
|
||||
ActorRef router7 =
|
||||
getContext().actorOf(FromConfig.getInstance().props(), "router7");
|
||||
//#random-group-1
|
||||
|
||||
//#random-group-2
|
||||
ActorRef router8 =
|
||||
getContext().actorOf(new RandomGroup(paths).props(), "router8");
|
||||
//#random-group-2
|
||||
|
||||
//#smallest-mailbox-pool-1
|
||||
ActorRef router9 =
|
||||
getContext().actorOf(FromConfig.getInstance().props(
|
||||
Props.create(Worker.class)), "router9");
|
||||
//#smallest-mailbox-pool-1
|
||||
|
||||
//#smallest-mailbox-pool-2
|
||||
ActorRef router10 =
|
||||
getContext().actorOf(new SmallestMailboxPool(5).props(
|
||||
Props.create(Worker.class)), "router10");
|
||||
//#smallest-mailbox-pool-2
|
||||
|
||||
//#broadcast-pool-1
|
||||
ActorRef router11 =
|
||||
getContext().actorOf(FromConfig.getInstance().props(
|
||||
Props.create(Worker.class)), "router11");
|
||||
//#broadcast-pool-1
|
||||
|
||||
//#broadcast-pool-2
|
||||
ActorRef router12 =
|
||||
getContext().actorOf(new BroadcastPool(5).props(Props.create(Worker.class)),
|
||||
"router12");
|
||||
//#broadcast-pool-2
|
||||
|
||||
//#broadcast-group-1
|
||||
ActorRef router13 =
|
||||
getContext().actorOf(FromConfig.getInstance().props(), "router13");
|
||||
//#broadcast-group-1
|
||||
|
||||
//#broadcast-group-2
|
||||
ActorRef router14 =
|
||||
getContext().actorOf(new BroadcastGroup(paths).props(), "router14");
|
||||
//#broadcast-group-2
|
||||
|
||||
//#scatter-gather-pool-1
|
||||
ActorRef router15 =
|
||||
getContext().actorOf(FromConfig.getInstance().props(
|
||||
Props.create(Worker.class)), "router15");
|
||||
//#scatter-gather-pool-1
|
||||
|
||||
//#scatter-gather-pool-2
|
||||
FiniteDuration within = FiniteDuration.create(10, TimeUnit.SECONDS);
|
||||
ActorRef router16 =
|
||||
getContext().actorOf(new ScatterGatherFirstCompletedPool(5, within).props(
|
||||
Props.create(Worker.class)), "router16");
|
||||
//#scatter-gather-pool-2
|
||||
|
||||
//#scatter-gather-group-1
|
||||
ActorRef router17 =
|
||||
getContext().actorOf(FromConfig.getInstance().props(), "router17");
|
||||
//#scatter-gather-group-1
|
||||
|
||||
//#scatter-gather-group-2
|
||||
FiniteDuration within2 = FiniteDuration.create(10, TimeUnit.SECONDS);
|
||||
ActorRef router18 =
|
||||
getContext().actorOf(new ScatterGatherFirstCompletedGroup(paths, within2).props(),
|
||||
"router18");
|
||||
//#scatter-gather-group-2
|
||||
|
||||
//#consistent-hashing-pool-1
|
||||
ActorRef router19 =
|
||||
getContext().actorOf(FromConfig.getInstance().props(Props.create(Worker.class)),
|
||||
"router19");
|
||||
//#consistent-hashing-pool-1
|
||||
|
||||
//#consistent-hashing-pool-2
|
||||
ActorRef router20 =
|
||||
getContext().actorOf(new ConsistentHashingPool(5).props(
|
||||
Props.create(Worker.class)), "router20");
|
||||
//#consistent-hashing-pool-2
|
||||
|
||||
//#consistent-hashing-group-1
|
||||
ActorRef router21 =
|
||||
getContext().actorOf(FromConfig.getInstance().props(), "router21");
|
||||
//#consistent-hashing-group-1
|
||||
|
||||
//#consistent-hashing-group-2
|
||||
ActorRef router22 =
|
||||
getContext().actorOf(new ConsistentHashingGroup(paths).props(), "router22");
|
||||
//#consistent-hashing-group-2
|
||||
|
||||
//#resize-pool-1
|
||||
ActorRef router23 =
|
||||
getContext().actorOf(FromConfig.getInstance().props(
|
||||
Props.create(Worker.class)), "router23");
|
||||
//#resize-pool-1
|
||||
|
||||
//#resize-pool-2
|
||||
DefaultResizer resizer = new DefaultResizer(2, 15);
|
||||
ActorRef router24 =
|
||||
getContext().actorOf(new RoundRobinPool(5).withResizer(resizer).props(
|
||||
Props.create(Worker.class)), "router24");
|
||||
//#resize-pool-2
|
||||
|
||||
public void onReceive(Object msg) {}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@Test
|
||||
public void createActors() {
|
||||
//#create-workers
|
||||
system.actorOf(Props.create(Workers.class), "workers");
|
||||
//#create-workers
|
||||
|
||||
//#create-parent
|
||||
system.actorOf(Props.create(Parent.class), "parent");
|
||||
//#create-parent
|
||||
}
|
||||
|
||||
@Test
|
||||
public void demonstrateDispatcher() {
|
||||
//#dispatchers
|
||||
Props props =
|
||||
// “head” will run on "router-dispatcher" dispatcher
|
||||
new RoundRobinPool(5).withDispatcher("router-dispatcher").props(
|
||||
Props.create(Worker.class))
|
||||
// Worker routees will run on "workers-dispatcher" dispatcher
|
||||
.withDispatcher("workers-dispatcher");
|
||||
ActorRef router = system.actorOf(props);
|
||||
//#dispatchers
|
||||
}
|
||||
|
||||
@Test
|
||||
public void demonstrateBroadcast() {
|
||||
new JavaTestKit(system) {{
|
||||
ActorRef router = system.actorOf(new RoundRobinPool(5).props(
|
||||
Props.create(Echo.class)));
|
||||
//#broadcastDavyJonesWarning
|
||||
router.tell(new Broadcast("Watch out for Davy Jones' locker"), getTestActor());
|
||||
//#broadcastDavyJonesWarning
|
||||
assertEquals(5, receiveN(5).length);
|
||||
}};
|
||||
}
|
||||
|
||||
@Test
|
||||
public void demonstratePoisonPill() {
|
||||
new JavaTestKit(system) {{
|
||||
ActorRef router = watch(system.actorOf(new RoundRobinPool(5).props(
|
||||
Props.create(Echo.class))));
|
||||
//#poisonPill
|
||||
router.tell(PoisonPill.getInstance(), getTestActor());
|
||||
//#poisonPill
|
||||
expectTerminated(router);
|
||||
}};
|
||||
}
|
||||
|
||||
@Test
|
||||
public void demonstrateBroadcastPoisonPill() {
|
||||
new JavaTestKit(system) {{
|
||||
ActorRef router = watch(system.actorOf(new RoundRobinPool(5).props(
|
||||
Props.create(Echo.class))));
|
||||
//#broadcastPoisonPill
|
||||
router.tell(new Broadcast(PoisonPill.getInstance()), getTestActor());
|
||||
//#broadcastPoisonPill
|
||||
expectTerminated(router);
|
||||
}};
|
||||
}
|
||||
|
||||
@Test
|
||||
public void demonstrateKill() {
|
||||
new JavaTestKit(system) {{
|
||||
ActorRef router = watch(system.actorOf(new RoundRobinPool(5).props(
|
||||
Props.create(Echo.class))));
|
||||
//#kill
|
||||
router.tell(Kill.getInstance(), getTestActor());
|
||||
//#kill
|
||||
expectTerminated(router);
|
||||
}};
|
||||
}
|
||||
|
||||
@Test
|
||||
public void demonstrateBroadcastKill() {
|
||||
new JavaTestKit(system) {{
|
||||
ActorRef router = watch(system.actorOf(new RoundRobinPool(5).props(
|
||||
Props.create(Echo.class))));
|
||||
//#broadcastKill
|
||||
router.tell(new Broadcast(Kill.getInstance()), getTestActor());
|
||||
//#broadcastKill
|
||||
expectTerminated(router);
|
||||
}};
|
||||
}
|
||||
|
||||
@Test
|
||||
public void demonstrateRemoteDeploy() {
|
||||
//#remoteRoutees
|
||||
Address[] addresses = {
|
||||
new Address("akka", "remotesys", "otherhost", 1234),
|
||||
AddressFromURIString.parse("akka://othersys@anotherhost:1234")};
|
||||
ActorRef routerRemote = system.actorOf(
|
||||
new RemoteRouterConfig(new RoundRobinPool(5), addresses).props(
|
||||
Props.create(Echo.class)));
|
||||
//#remoteRoutees
|
||||
}
|
||||
|
||||
@Test
|
||||
public void demonstrateSupervisor() {
|
||||
//#supervision
|
||||
final SupervisorStrategy strategy =
|
||||
new OneForOneStrategy(5, Duration.create(1, TimeUnit.MINUTES),
|
||||
Collections.<Class<? extends Throwable>>singletonList(Exception.class));
|
||||
final ActorRef router = system.actorOf(new RoundRobinPool(5).
|
||||
withSupervisorStrategy(strategy).props(Props.create(Echo.class)));
|
||||
//#supervision
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,61 +0,0 @@
|
|||
/**
|
||||
* Copyright (C) 2009-2013 Typesafe Inc. <http://www.typesafe.com>
|
||||
*/
|
||||
package 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(
|
||||
Props.create(ExampleActor.class).withRouter(new FromConfig()), "myrouter1");
|
||||
//#configurableRouting
|
||||
for (int i = 1; i <= 10; i++) {
|
||||
router.tell(new ExampleActor.Message(i), ActorRef.noSender());
|
||||
}
|
||||
|
||||
//#configurableRoutingWithResizer
|
||||
ActorRef router2 = system.actorOf(
|
||||
Props.create(ExampleActor.class).withRouter(new FromConfig()), "myrouter2");
|
||||
//#configurableRoutingWithResizer
|
||||
for (int i = 1; i <= 10; i++) {
|
||||
router2.tell(new ExampleActor.Message(i), ActorRef.noSender());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,122 +0,0 @@
|
|||
/**
|
||||
* Copyright (C) 2009-2013 Typesafe Inc. <http://www.typesafe.com>
|
||||
*/
|
||||
package docs.jrouting;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
import akka.testkit.AkkaJUnitActorSystemResource;
|
||||
import org.junit.ClassRule;
|
||||
import org.junit.Test;
|
||||
|
||||
import akka.actor.ActorRef;
|
||||
import akka.actor.ActorSystem;
|
||||
import akka.actor.Kill;
|
||||
import akka.actor.PoisonPill;
|
||||
import akka.actor.Props;
|
||||
import akka.actor.Terminated;
|
||||
import akka.routing.Broadcast;
|
||||
import akka.routing.RoundRobinRouter;
|
||||
import akka.testkit.JavaTestKit;
|
||||
import docs.jrouting.RouterViaProgramExample.ExampleActor;
|
||||
import docs.routing.RouterViaProgramDocSpec.Echo;
|
||||
|
||||
public class RouterViaProgramDocTest {
|
||||
|
||||
@ClassRule
|
||||
public static AkkaJUnitActorSystemResource actorSystemResource =
|
||||
new AkkaJUnitActorSystemResource("RouterViaProgramDocTest");
|
||||
|
||||
private final ActorSystem system = actorSystemResource.getSystem();
|
||||
|
||||
private static class JavaTestKitWithSelf extends JavaTestKit {
|
||||
public JavaTestKitWithSelf(ActorSystem system) {
|
||||
super(system);
|
||||
}
|
||||
/**
|
||||
* Wrap `getRef()` so our examples look like they're within a normal actor.
|
||||
*/
|
||||
public ActorRef getSelf() {
|
||||
return getRef();
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
@Test
|
||||
public void demonstrateRouteesFromPaths() {
|
||||
new JavaTestKit(system) {{
|
||||
//#programmaticRoutingRouteePaths
|
||||
ActorRef actor1 = system.actorOf(Props.create(ExampleActor.class), "actor1");
|
||||
ActorRef actor2 = system.actorOf(Props.create(ExampleActor.class), "actor2");
|
||||
ActorRef actor3 = system.actorOf(Props.create(ExampleActor.class), "actor3");
|
||||
Iterable<String> routees = Arrays.asList(
|
||||
new String[] { "/user/actor1", "/user/actor2", "/user/actor3" });
|
||||
ActorRef router = system.actorOf(
|
||||
Props.empty().withRouter(new RoundRobinRouter(routees)));
|
||||
//#programmaticRoutingRouteePaths
|
||||
for (int i = 1; i <= 6; i++) {
|
||||
router.tell(new ExampleActor.Message(i), ActorRef.noSender());
|
||||
}
|
||||
}};
|
||||
}
|
||||
|
||||
@Test
|
||||
public void demonstrateBroadcast() {
|
||||
new JavaTestKitWithSelf(system) {{
|
||||
ActorRef router = system.actorOf(Props.create(Echo.class).withRouter(new RoundRobinRouter(5)));
|
||||
//#broadcastDavyJonesWarning
|
||||
router.tell(new Broadcast("Watch out for Davy Jones' locker"), getSelf());
|
||||
//#broadcastDavyJonesWarning
|
||||
receiveN(5, duration("5 seconds"));
|
||||
}};
|
||||
}
|
||||
|
||||
@Test
|
||||
public void demonstratePoisonPill() {
|
||||
new JavaTestKitWithSelf(system) {{
|
||||
ActorRef router = system.actorOf(Props.create(Echo.class).withRouter(new RoundRobinRouter(5)));
|
||||
watch(router);
|
||||
//#poisonPill
|
||||
router.tell(PoisonPill.getInstance(), getSelf());
|
||||
//#poisonPill
|
||||
expectMsgClass(Terminated.class);
|
||||
}};
|
||||
}
|
||||
|
||||
@Test
|
||||
public void demonstrateBroadcastOfPoisonPill() {
|
||||
new JavaTestKitWithSelf(system) {{
|
||||
ActorRef router = system.actorOf(Props.create(Echo.class).withRouter(new RoundRobinRouter(5)));
|
||||
watch(router);
|
||||
//#broadcastPoisonPill
|
||||
router.tell(new Broadcast(PoisonPill.getInstance()), getSelf());
|
||||
//#broadcastPoisonPill
|
||||
expectMsgClass(Terminated.class);
|
||||
}};
|
||||
}
|
||||
|
||||
@Test
|
||||
public void demonstrateKill() {
|
||||
new JavaTestKitWithSelf(system) {{
|
||||
ActorRef router = system.actorOf(Props.create(Echo.class).withRouter(new RoundRobinRouter(5)));
|
||||
watch(router);
|
||||
//#kill
|
||||
router.tell(Kill.getInstance(), getSelf());
|
||||
//#kill
|
||||
expectMsgClass(Terminated.class);
|
||||
}};
|
||||
}
|
||||
|
||||
@Test
|
||||
public void demonstrateBroadcastOfKill() {
|
||||
new JavaTestKitWithSelf(system) {{
|
||||
ActorRef router = system.actorOf(Props.create(Echo.class).withRouter(new RoundRobinRouter(5)));
|
||||
watch(router);
|
||||
//#broadcastKill
|
||||
router.tell(new Broadcast(Kill.getInstance()), getSelf());
|
||||
//#broadcastKill
|
||||
expectMsgClass(Terminated.class);
|
||||
}};
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,103 +0,0 @@
|
|||
/**
|
||||
* Copyright (C) 2009-2013 Typesafe Inc. <http://www.typesafe.com>
|
||||
*/
|
||||
package docs.jrouting;
|
||||
|
||||
import akka.routing.RoundRobinRouter;
|
||||
import akka.routing.DefaultResizer;
|
||||
import akka.remote.routing.RemoteRouterConfig;
|
||||
import akka.actor.ActorRef;
|
||||
import akka.actor.Props;
|
||||
import akka.actor.UntypedActor;
|
||||
import akka.actor.ActorSystem;
|
||||
import akka.actor.Address;
|
||||
import akka.actor.AddressFromURIString;
|
||||
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;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
public static void main(String... args) {
|
||||
ActorSystem system = ActorSystem.create("RPE");
|
||||
//#programmaticRoutingNrOfInstances
|
||||
int nrOfInstances = 5;
|
||||
ActorRef router1 = system.actorOf(
|
||||
Props.create(ExampleActor.class).withRouter(new RoundRobinRouter(nrOfInstances)));
|
||||
//#programmaticRoutingNrOfInstances
|
||||
for (int i = 1; i <= 6; i++) {
|
||||
router1.tell(new ExampleActor.Message(i), ActorRef.noSender());
|
||||
}
|
||||
|
||||
//#programmaticRoutingRoutees
|
||||
ActorRef actor1 = system.actorOf(Props.create(ExampleActor.class));
|
||||
ActorRef actor2 = system.actorOf(Props.create(ExampleActor.class));
|
||||
ActorRef actor3 = system.actorOf(Props.create(ExampleActor.class));
|
||||
Iterable<ActorRef> routees = Arrays.asList(
|
||||
new ActorRef[] { actor1, actor2, actor3 });
|
||||
ActorRef router2 = system.actorOf(
|
||||
Props.empty().withRouter(RoundRobinRouter.create(routees)));
|
||||
//#programmaticRoutingRoutees
|
||||
for (int i = 1; i <= 6; i++) {
|
||||
router2.tell(new ExampleActor.Message(i), ActorRef.noSender());
|
||||
}
|
||||
|
||||
//#programmaticRoutingWithResizer
|
||||
int lowerBound = 2;
|
||||
int upperBound = 15;
|
||||
DefaultResizer resizer = new DefaultResizer(lowerBound, upperBound);
|
||||
ActorRef router3 = system.actorOf(
|
||||
Props.create(ExampleActor.class).withRouter(new RoundRobinRouter(resizer)));
|
||||
//#programmaticRoutingWithResizer
|
||||
for (int i = 1; i <= 6; i++) {
|
||||
router3.tell(new ExampleActor.Message(i), ActorRef.noSender());
|
||||
}
|
||||
|
||||
//#remoteRoutees
|
||||
Address addr1 = new Address("akka", "remotesys", "otherhost", 1234);
|
||||
Address addr2 = AddressFromURIString.parse("akka://othersys@anotherhost:1234");
|
||||
Address[] addresses = new Address[] { addr1, addr2 };
|
||||
ActorRef routerRemote = system.actorOf(Props.create(ExampleActor.class)
|
||||
.withRouter(new RemoteRouterConfig(new RoundRobinRouter(5), addresses)));
|
||||
//#remoteRoutees
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
private class CompileCheckJavaDocsForRouting extends UntypedActor {
|
||||
|
||||
@Override
|
||||
public void onReceive(Object o) {
|
||||
//#reply-with-parent
|
||||
getSender().tell("reply", getContext().parent()); // replies go to router
|
||||
//#reply-with-parent
|
||||
//#reply-with-self
|
||||
getSender().tell("reply", getSelf()); // replies go to this actor
|
||||
//#reply-with-self
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
|
@ -233,7 +233,7 @@ This is also done via configuration::
|
|||
actor {
|
||||
deployment {
|
||||
/serviceA/aggregation {
|
||||
router = "round-robin"
|
||||
router = "round-robin-pool"
|
||||
nr-of-instances = 10
|
||||
target {
|
||||
nodes = ["akka.tcp://app@10.0.0.2:2552", "akka://app@10.0.0.3:2552"]
|
||||
|
|
|
|||
|
|
@ -2,125 +2,132 @@
|
|||
.. _routing-java:
|
||||
|
||||
Routing
|
||||
==============
|
||||
=======
|
||||
|
||||
A Router is an actor that receives messages and efficiently routes them to other actors, known as
|
||||
its *routees*.
|
||||
Messages can be sent via a router to efficiently route them to destination actors, known as
|
||||
its *routees*. A ``Router`` can be used inside or outside of an actor, and you can manage the
|
||||
routees yourselves or use a self contained router actor with configuration capabilities.
|
||||
|
||||
Different routing strategies can be used, according to your application's needs. Akka comes with
|
||||
several useful routing strategies right out of the box. But, as you will see in this chapter, it is
|
||||
also possible to :ref:`create your own <custom-router-java>`.
|
||||
|
||||
The routers shipped with Akka are:
|
||||
.. _simple-router-java:
|
||||
|
||||
* ``akka.routing.RoundRobinRouter``
|
||||
* ``akka.routing.RandomRouter``
|
||||
* ``akka.routing.SmallestMailboxRouter``
|
||||
* ``akka.routing.BroadcastRouter``
|
||||
* ``akka.routing.ScatterGatherFirstCompletedRouter``
|
||||
* ``akka.routing.ConsistentHashingRouter``
|
||||
A Simple Router
|
||||
^^^^^^^^^^^^^^^
|
||||
|
||||
Routers in Action
|
||||
^^^^^^^^^^^^^^^^^
|
||||
The following example illustrates how to use a ``Router`` and manage the routees from within an actor.
|
||||
|
||||
Sending a message to a router is easy.
|
||||
.. includecode:: code/docs/jrouting/RouterDocTest.java#router-in-actor
|
||||
|
||||
.. code-block:: java
|
||||
We create a ``Router`` and specify that it should use ``RoundRobinRoutingLogic`` when routing the
|
||||
messages to the routees.
|
||||
|
||||
router.tell(new MyMsg(), sender);
|
||||
The routing logic shipped with Akka are:
|
||||
|
||||
A router actor forwards messages to its routees according to its routing policy.
|
||||
* ``akka.routing.RoundRobinRoutingLogic``
|
||||
* ``akka.routing.RandomRoutingLogic``
|
||||
* ``akka.routing.SmallestMailboxRoutingLogic``
|
||||
* ``akka.routing.BroadcastRoutingLogic``
|
||||
* ``akka.routing.ScatterGatherFirstCompletedRoutingLogic``
|
||||
* ``akka.routing.ConsistentHashingRoutingLogic``
|
||||
|
||||
We create the routees as ordinary child actors wrapped in ``ActorRefRoutee``. We watch
|
||||
the routees to be able to replace them if they are terminated.
|
||||
|
||||
Sending messages via the router is done with the ``route`` method, as is done for the ``Work`` messages
|
||||
in the example above.
|
||||
|
||||
The ``Router`` is immutable and the ``RoutingLogic`` is thread safe; meaning that they can also be used
|
||||
outside of actors.
|
||||
|
||||
.. note::
|
||||
|
||||
In general, any message sent to a router will be sent onwards to its routees. But there are a
|
||||
In general, any message sent to a router will be sent onwards to its routees, but there is one exception.
|
||||
The special :ref:`broadcast-messages-java` will send to *all* of a router's routees
|
||||
|
||||
A Router Actor
|
||||
^^^^^^^^^^^^^^
|
||||
|
||||
A router can also be created as a self contained actor that manages the routees itself and
|
||||
loads routing logic and other settings from configuration.
|
||||
|
||||
This type of router actor comes in two distinct flavors:
|
||||
|
||||
* Pool - The router creates routees as child actors and removes them from the router if they
|
||||
terminate.
|
||||
|
||||
* Group - The routee actors are created externally to the router and the router sends
|
||||
messages to the specified path using actor selection, without watching for termination.
|
||||
|
||||
The settings for a router actor can be defined in configuration or programmatically.
|
||||
Although router actors can be defined in the configuration file, they must still be created
|
||||
programmatically, i.e. you cannot make a router through external configuration alone.
|
||||
If you define the router actor in the configuration file then these settings will be used
|
||||
instead of any programmatically provided parameters.
|
||||
|
||||
You send messages to the routees via the router actor in the same way as for ordinary actors,
|
||||
i.e. via its ``ActorRef``. The router actor forwards messages onto its routees without changing
|
||||
the original sender. When a routee replies to a routed message, the reply will be sent to the
|
||||
original sender, not to the router actor.
|
||||
|
||||
.. note::
|
||||
|
||||
In general, any message sent to a router will be sent onwards to its routees, but there are a
|
||||
few exceptions. These are documented in the :ref:`router-special-messages-java` section below.
|
||||
|
||||
Creating a Router
|
||||
*****************
|
||||
Pool
|
||||
----
|
||||
|
||||
Routers and routees are closely intertwined. Router actors are created by specifying the desired
|
||||
*routee* :class:`Props` then attaching the router's :class:`RouterConfig`. When you create a router
|
||||
actor it will create routees, as needed, as its children.
|
||||
|
||||
For example, the following code and configuration snippets show how to create a :ref:`round-robin
|
||||
<round-robin-router-java>` router that forwards messages to five ``ExampleActor`` routees. The
|
||||
The following code and configuration snippets show how to create a :ref:`round-robin
|
||||
<round-robin-router-java>` router that forwards messages to five ``Worker`` routees. The
|
||||
routees will be created as the router's children.
|
||||
|
||||
.. includecode:: ../scala/code/docs/routing/RouterViaConfigDocSpec.scala#config-round-robin
|
||||
.. includecode:: ../scala/code/docs/routing/RouterDocSpec.scala#config-round-robin-pool
|
||||
|
||||
.. includecode:: code/docs/jrouting/RouterViaConfigExample.java#configurableRouting
|
||||
.. includecode:: code/docs/jrouting/RouterDocTest.java#round-robin-pool-1
|
||||
|
||||
Here is the same example, but with the router configuration provided programmatically instead of
|
||||
from configuration.
|
||||
|
||||
.. includecode:: code/docs/jrouting/RouterViaProgramExample.java#programmaticRoutingNrOfInstances
|
||||
.. includecode:: code/docs/jrouting/RouterDocTest.java#round-robin-pool-2
|
||||
|
||||
Sometimes, rather than having the router create its routees, it is desirable to create routees
|
||||
separately and provide them to the router for its use. You can do this by passing an
|
||||
:class:`Iterable` of routees to the router's configuration.
|
||||
Remote Deployed Routees
|
||||
***********************
|
||||
|
||||
The example below shows how to create a router by providing it with the :class:`ActorRef`\s of three
|
||||
routee actors.
|
||||
|
||||
.. includecode:: code/docs/jrouting/RouterViaProgramExample.java#programmaticRoutingRoutees
|
||||
|
||||
Routees can also be specified by providing their path strings instead of their :class:`ActorRef`\s.
|
||||
|
||||
.. includecode:: code/docs/jrouting/RouterViaProgramDocTest.java#programmaticRoutingRouteePaths
|
||||
|
||||
In addition to being able to supply looked-up remote actors as routees, you can ask the router to
|
||||
In addition to being able to create local actors as routees, you can instruct the router to
|
||||
deploy its created children on a set of remote hosts. Routees will be deployed in round-robin
|
||||
fashion. In order to deploy routees remotely, wrap the router configuration in a
|
||||
:class:`RemoteRouterConfig`, attaching the remote addresses of the nodes to deploy to. Remote
|
||||
``RemoteRouterConfig``, attaching the remote addresses of the nodes to deploy to. Remote
|
||||
deployment requires the ``akka-remote`` module to be included in the classpath.
|
||||
|
||||
.. includecode:: code/docs/jrouting/RouterViaProgramExample.java#remoteRoutees
|
||||
.. includecode:: code/docs/jrouting/RouterDocTest.java#remoteRoutees
|
||||
|
||||
There are a few gotchas to be aware of when creating routers:
|
||||
Senders
|
||||
*******
|
||||
|
||||
* If you define the ``router`` in the configuration file then this value will be used instead of any
|
||||
programmatically provided parameters.
|
||||
* Although routers can be configured in the configuration file, they must still be created
|
||||
programmatically, i.e. you cannot make a router through external configuration alone.
|
||||
* If you provide the ``routees`` in the router configuration then
|
||||
the value of ``nrOfInstances``, if provided, will be disregarded.
|
||||
* When you provide routees programmatically the router will generally ignore the routee
|
||||
:class:`Props`, as it does not need to create routees. However, if you use a :ref:`resizable
|
||||
router <resizable-routers-java>` then the routee :class:`Props` will be used whenever the
|
||||
resizer creates new routees.
|
||||
|
||||
Routers, Routees and Senders
|
||||
****************************
|
||||
When a routee sends a message, it can :ref:`set itself as the sender
|
||||
<actors-tell-sender-java>`.
|
||||
|
||||
The router forwards messages onto its routees without changing the original sender. When a routee
|
||||
replies to a routed message, the reply will be sent to the original sender, not to the router.
|
||||
.. includecode:: code/docs/jrouting/RouterDocTest.java#reply-with-self
|
||||
|
||||
When a router creates routees, they are created as the routers children. This gives each routee its
|
||||
own identity in the actor system.
|
||||
However, it is often useful for routees to set the *router* as a sender. For example, you might want
|
||||
to set the router as the sender if you want to hide the details of the routees behind the router.
|
||||
The following code snippet shows how to set the parent router as sender.
|
||||
|
||||
When a routee replies it can :ref:`set itself <actors-tell-sender-java>` as the sender of the reply.
|
||||
.. includecode:: code/docs/jrouting/RouterDocTest.java#reply-with-parent
|
||||
|
||||
.. includecode:: code/docs/jrouting/RouterViaProgramExample.java#reply-with-self
|
||||
|
||||
However, it is often useful for routees to set the *router* as the sender. For example, you might
|
||||
want to set the router as the sender if you want to hide the details of the routees behind the
|
||||
router. The following code snippet shows how to set the parent router as sender.
|
||||
Supervision
|
||||
***********
|
||||
|
||||
.. includecode:: code/docs/jrouting/RouterViaProgramExample.java#reply-with-parent
|
||||
|
||||
Note that different code would be needed if the routees were not children of the router, i.e. if
|
||||
they were provided when the router was created.
|
||||
|
||||
Routers and Supervision
|
||||
^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
Routees can be created by a router or provided to the router when it is created. Any routees that
|
||||
are created by a router will be created as the router's children. The router is therefore also the
|
||||
children's supervisor.
|
||||
Routees that are created by a pool router will be created as the router's children. The router is
|
||||
therefore also the children's supervisor.
|
||||
|
||||
The supervision strategy of the router actor can be configured with the
|
||||
:meth:`RouterConfig.supervisorStrategy` property. If no configuration is provided, routers default
|
||||
``supervisorStrategy`` property of the Pool. If no configuration is provided, routers default
|
||||
to a strategy of “always escalate”. This means that errors are passed up to the router's supervisor
|
||||
for handling. The router's supervisor will decide what to do about any errors.
|
||||
|
||||
|
|
@ -140,97 +147,116 @@ by specifying the strategy when defining the router.
|
|||
|
||||
Setting the strategy is easily done:
|
||||
|
||||
.. includecode:: code/docs/jrouting/CustomRouterDocTest.java
|
||||
:include: supervision
|
||||
.. includecode:: code/docs/jrouting/RouterDocTest.java#supervision
|
||||
|
||||
.. _note-router-terminated-children-java:
|
||||
|
||||
.. note::
|
||||
|
||||
If the child of a router terminates, the router will not automatically spawn
|
||||
a new child. In the event that all children of a router have terminated the
|
||||
router will terminate itself, unless it is a dynamic router, e.g. using
|
||||
If the child of a pool router terminates, the pool router will not automatically spawn
|
||||
a new child. In the event that all children of a pool router have terminated the
|
||||
router will terminate itself unless it is a dynamic router, e.g. using
|
||||
a resizer.
|
||||
|
||||
Group
|
||||
-----
|
||||
|
||||
Sometimes, rather than having the router actor create its routees, it is desirable to create routees
|
||||
separately and provide them to the router for its use. You can do this by passing an
|
||||
paths of the routees to the router's configuration. Messages will be sent with ``ActorSelection``
|
||||
to these paths.
|
||||
|
||||
The example below shows how to create a router by providing it with the path strings of three
|
||||
routee actors.
|
||||
|
||||
.. includecode:: ../scala/code/docs/routing/RouterDocSpec.scala#config-round-robin-group
|
||||
|
||||
.. includecode:: code/docs/jrouting/RouterDocTest.java#round-robin-group-1
|
||||
|
||||
Here is the same example, but with the router configuration provided programmatically instead of
|
||||
from configuration.
|
||||
|
||||
.. includecode:: code/docs/jrouting/RouterDocTest.java#round-robin-group-2
|
||||
|
||||
The routee actors are created externally from the router:
|
||||
|
||||
.. includecode:: code/docs/jrouting/RouterDocTest.java#create-workers
|
||||
|
||||
.. includecode:: code/docs/jrouting/RouterDocTest.java#create-worker-actors
|
||||
|
||||
The paths may contain protocol and address information for actors running on remote hosts.
|
||||
Remoting requires the ``akka-remote`` module to be included in the classpath.
|
||||
|
||||
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:
|
||||
In this section we will describe how to create the different types of router actors.
|
||||
|
||||
.. includecode:: code/docs/jrouting/PrintlnActor.java#printlnActor
|
||||
The router actors in this section are created from within a top level actor named ``parent``.
|
||||
Note that deployment paths in the configuration starts with ``/parent/`` followed by the name
|
||||
of the router actor.
|
||||
|
||||
and
|
||||
|
||||
.. includecode:: code/docs/jrouting/FibonacciActor.java#fibonacciActor
|
||||
.. includecode:: code/docs/jrouting/RouterDocTest.java#create-parent
|
||||
|
||||
.. _round-robin-router-java:
|
||||
|
||||
RoundRobinRouter
|
||||
****************
|
||||
RoundRobinPool and RoundRobinGroup
|
||||
----------------------------------
|
||||
|
||||
Routes in a `round-robin <http://en.wikipedia.org/wiki/Round-robin>`_ fashion to its routees.
|
||||
Code example:
|
||||
|
||||
.. includecode:: code/docs/jrouting/ParentActor.java#roundRobinRouter
|
||||
RoundRobinPool defined in configuration:
|
||||
|
||||
When run you should see a similar output to this:
|
||||
.. includecode:: ../scala/code/docs/routing/RouterDocSpec.scala#config-round-robin-pool
|
||||
|
||||
.. code-block:: scala
|
||||
.. includecode:: code/docs/jrouting/RouterDocTest.java#round-robin-pool-1
|
||||
|
||||
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
|
||||
RoundRobinPool defined in code:
|
||||
|
||||
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.)
|
||||
.. includecode:: code/docs/jrouting/RouterDocTest.java#round-robin-pool-2
|
||||
|
||||
This is an example of how to define a round-robin router in configuration:
|
||||
RoundRobinGroup defined in configuration:
|
||||
|
||||
.. includecode:: ../scala/code/docs/routing/RouterViaConfigDocSpec.scala#config-round-robin
|
||||
.. includecode:: ../scala/code/docs/routing/RouterDocSpec.scala#config-round-robin-group
|
||||
|
||||
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/docs/jrouting/RouterDocTest.java#round-robin-group-1
|
||||
|
||||
.. includecode:: code/docs/jrouting/ParentActor.java#randomRouter
|
||||
RoundRobinGroup defined in code:
|
||||
|
||||
When run you should see a similar output to this:
|
||||
.. includecode:: code/docs/jrouting/RouterDocTest.java
|
||||
:include: paths,round-robin-group-2
|
||||
|
||||
.. code-block:: scala
|
||||
RandomPool and RandomGroup
|
||||
--------------------------
|
||||
|
||||
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
|
||||
This router type selects one of its routees randomly for each message.
|
||||
|
||||
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.
|
||||
RandomPool defined in configuration:
|
||||
|
||||
This is an example of how to define a random router in configuration:
|
||||
.. includecode:: ../scala/code/docs/routing/RouterDocSpec.scala#config-random-pool
|
||||
|
||||
.. includecode:: ../scala/code/docs/routing/RouterViaConfigDocSpec.scala#config-random
|
||||
.. includecode:: code/docs/jrouting/RouterDocTest.java#random-pool-1
|
||||
|
||||
SmallestMailboxRouter
|
||||
*********************
|
||||
A Router that tries to send to the non-suspended routee with fewest messages in mailbox.
|
||||
RandomPool defined in code:
|
||||
|
||||
.. includecode:: code/docs/jrouting/RouterDocTest.java#random-pool-2
|
||||
|
||||
RandomGroup defined in configuration:
|
||||
|
||||
.. includecode:: ../scala/code/docs/routing/RouterDocSpec.scala#config-random-group
|
||||
|
||||
.. includecode:: code/docs/jrouting/RouterDocTest.java#random-group-1
|
||||
|
||||
RandomGroup defined in code:
|
||||
|
||||
.. includecode:: code/docs/jrouting/RouterDocTest.java
|
||||
:include: paths,random-group-2
|
||||
|
||||
|
||||
SmallestMailboxPool
|
||||
-------------------
|
||||
|
||||
A Router that tries to send to the non-suspended child routee with fewest messages in mailbox.
|
||||
The selection is done in this order:
|
||||
|
||||
* pick any idle routee (not processing message) with empty mailbox
|
||||
|
|
@ -239,36 +265,45 @@ The selection is done in this order:
|
|||
* pick any remote routee, remote actors are consider lowest priority,
|
||||
since their mailbox size is unknown
|
||||
|
||||
Code example:
|
||||
SmallestMailboxPool defined in configuration:
|
||||
|
||||
.. includecode:: code/docs/jrouting/ParentActor.java#smallestMailboxRouter
|
||||
.. includecode:: ../scala/code/docs/routing/RouterDocSpec.scala#config-smallest-mailbox-pool
|
||||
|
||||
This is an example of how to define a smallest-mailbox router in configuration:
|
||||
.. includecode:: code/docs/jrouting/RouterDocTest.java#smallest-mailbox-pool-1
|
||||
|
||||
.. includecode:: ../scala/code/docs/routing/RouterViaConfigDocSpec.scala#config-smallest-mailbox
|
||||
SmallestMailboxPool defined in code:
|
||||
|
||||
.. includecode:: code/docs/jrouting/RouterDocTest.java#smallest-mailbox-pool-2
|
||||
|
||||
There is no Group variant of the SmallestMailboxPool because the size of the mailbox
|
||||
and the internal dispatching state of the actor is not practically available from the paths
|
||||
of the routees.
|
||||
|
||||
BroadcastPool and BroadcastGroup
|
||||
--------------------------------
|
||||
|
||||
BroadcastRouter
|
||||
***************
|
||||
A broadcast router forwards the message it receives to *all* its routees.
|
||||
Code example:
|
||||
|
||||
.. includecode:: code/docs/jrouting/ParentActor.java#broadcastRouter
|
||||
BroadcastPool defined in configuration:
|
||||
|
||||
When run you should see a similar output to this:
|
||||
.. includecode:: ../scala/code/docs/routing/RouterDocSpec.scala#config-broadcast-pool
|
||||
|
||||
.. code-block:: scala
|
||||
.. includecode:: code/docs/jrouting/RouterDocTest.java#broadcast-pool-1
|
||||
|
||||
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
|
||||
BroadcastPool defined in code:
|
||||
|
||||
As you can see here above each of the routees, five in total, received the broadcast message.
|
||||
.. includecode:: code/docs/jrouting/RouterDocTest.java#broadcast-pool-2
|
||||
|
||||
This is an example of how to define a broadcast router in configuration:
|
||||
BroadcastGroup defined in configuration:
|
||||
|
||||
.. includecode:: ../scala/code/docs/routing/RouterViaConfigDocSpec.scala#config-broadcast
|
||||
.. includecode:: ../scala/code/docs/routing/RouterDocSpec.scala#config-broadcast-group
|
||||
|
||||
.. includecode:: code/docs/jrouting/RouterDocTest.java#broadcast-group-1
|
||||
|
||||
BroadcastGroup defined in code:
|
||||
|
||||
.. includecode:: code/docs/jrouting/RouterDocTest.java
|
||||
:include: paths,broadcast-group-2
|
||||
|
||||
.. note::
|
||||
|
||||
|
|
@ -276,39 +311,50 @@ This is an example of how to define a broadcast router in configuration:
|
|||
broadcast every message, then you can use a non-broadcasting router and use
|
||||
:ref:`broadcast-messages-java` as needed.
|
||||
|
||||
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/docs/jrouting/ParentActor.java#scatterGatherFirstCompletedRouter
|
||||
ScatterGatherFirstCompletedPool and ScatterGatherFirstCompletedGroup
|
||||
--------------------------------------------------------------------
|
||||
|
||||
When run you should see this:
|
||||
The ScatterGatherFirstCompletedRouter will send the message on to all its routees.
|
||||
It then waits for first reply it gets back. This result will be sent back to original sender.
|
||||
Other replies are discarded.
|
||||
|
||||
.. code-block:: scala
|
||||
It is expecting at least one reply within a configured duration, otherwise it will reply with
|
||||
``akka.pattern.AskTimeoutException`` in a ``akka.actor.Status.Failure``.
|
||||
|
||||
The result of calculating Fibonacci for 10 is 55
|
||||
ScatterGatherFirstCompletedPool defined in configuration:
|
||||
|
||||
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.
|
||||
.. includecode:: ../scala/code/docs/routing/RouterDocSpec.scala#config-scatter-gather-pool
|
||||
|
||||
This is an example of how to define a scatter-gather router in configuration:
|
||||
.. includecode:: code/docs/jrouting/RouterDocTest.java#scatter-gather-pool-1
|
||||
|
||||
.. includecode:: ../scala/code/docs/routing/RouterViaConfigDocSpec.scala#config-scatter-gather
|
||||
ScatterGatherFirstCompletedPool defined in code:
|
||||
|
||||
ConsistentHashingRouter
|
||||
***********************
|
||||
.. includecode:: code/docs/jrouting/RouterDocTest.java#scatter-gather-pool-2
|
||||
|
||||
The ConsistentHashingRouter uses `consistent hashing <http://en.wikipedia.org/wiki/Consistent_hashing>`_
|
||||
to select a connection based on the sent message. This
|
||||
ScatterGatherFirstCompletedGroup defined in configuration:
|
||||
|
||||
.. includecode:: ../scala/code/docs/routing/RouterDocSpec.scala#config-scatter-gather-group
|
||||
|
||||
.. includecode:: code/docs/jrouting/RouterDocTest.java#scatter-gather-group-1
|
||||
|
||||
ScatterGatherFirstCompletedGroup defined in code:
|
||||
|
||||
.. includecode:: code/docs/jrouting/RouterDocTest.java
|
||||
:include: paths,scatter-gather-group-2
|
||||
|
||||
ConsistentHashingPool and ConsistentHashingGroup
|
||||
------------------------------------------------
|
||||
|
||||
The ConsistentHashingPool uses `consistent hashing <http://en.wikipedia.org/wiki/Consistent_hashing>`_
|
||||
to select a routee based on the sent message. This
|
||||
`article <http://weblogs.java.net/blog/tomwhite/archive/2007/11/consistent_hash.html>`_ gives good
|
||||
insight into how consistent hashing is implemented.
|
||||
|
||||
There is 3 ways to define what data to use for the consistent hash key.
|
||||
|
||||
* You can define ``withHashMapper`` of the router to map incoming
|
||||
messages to their consistent hash key. This makes the the decision
|
||||
messages to their consistent hash key. This makes the decision
|
||||
transparent for the sender.
|
||||
|
||||
* The messages may implement ``akka.routing.ConsistentHashingRouter.ConsistentHashable``.
|
||||
|
|
@ -322,40 +368,58 @@ There is 3 ways to define what data to use for the consistent hash key.
|
|||
These ways to define the consistent hash key can be use together and at
|
||||
the same time for one router. The ``withHashMapper`` is tried first.
|
||||
|
||||
|
||||
Code example:
|
||||
|
||||
.. includecode:: code/docs/jrouting/ConsistentHashingRouterDocTest.java
|
||||
:include: imports1
|
||||
.. includecode:: code/docs/jrouting/ConsistentHashingRouterDocTest.java#cache-actor
|
||||
|
||||
.. includecode:: code/docs/jrouting/ConsistentHashingRouterDocTest.java
|
||||
:include: cache-actor
|
||||
|
||||
.. includecode:: code/docs/jrouting/ConsistentHashingRouterDocTest.java
|
||||
:include: imports2
|
||||
|
||||
.. includecode:: code/docs/jrouting/ConsistentHashingRouterDocTest.java
|
||||
:include: consistent-hashing-router
|
||||
.. includecode:: code/docs/jrouting/ConsistentHashingRouterDocTest.java#consistent-hashing-router
|
||||
|
||||
In the above example you see that the ``Get`` message implements ``ConsistentHashable`` itself,
|
||||
while the ``Entry`` message is wrapped in a ``ConsistentHashableEnvelope``. The ``Evict``
|
||||
message is handled by the ``withHashMapper``.
|
||||
message is handled by the ``hashMapping`` partial function.
|
||||
|
||||
This is an example of how to define a consistent-hashing router in configuration:
|
||||
ConsistentHashingPool defined in configuration:
|
||||
|
||||
.. includecode:: ../scala/code/docs/routing/RouterViaConfigDocSpec.scala#config-consistent-hashing
|
||||
.. includecode:: ../scala/code/docs/routing/RouterDocSpec.scala#config-consistent-hashing-pool
|
||||
|
||||
.. includecode:: code/docs/jrouting/RouterDocTest.java#consistent-hashing-pool-1
|
||||
|
||||
ConsistentHashingPool defined in code:
|
||||
|
||||
.. includecode:: code/docs/jrouting/RouterDocTest.java#consistent-hashing-pool-2
|
||||
|
||||
ConsistentHashingGroup defined in configuration:
|
||||
|
||||
.. includecode:: ../scala/code/docs/routing/RouterDocSpec.scala#config-consistent-hashing-group
|
||||
|
||||
.. includecode:: code/docs/jrouting/RouterDocTest.java#consistent-hashing-group-1
|
||||
|
||||
ConsistentHashingGroup defined in code:
|
||||
|
||||
.. includecode:: code/docs/jrouting/RouterDocTest.java
|
||||
:include: paths,consistent-hashing-group-2
|
||||
|
||||
|
||||
``virtual-nodes-factor`` is the number of virtual nodes per routee that is used in the
|
||||
consistent hash node ring to make the distribution more uniform.
|
||||
|
||||
.. _router-special-messages-java:
|
||||
|
||||
Handling for Special Messages
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
Specially Handled Messages
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
Most messages sent to routers will be forwarded according to the routers' usual routing rules.
|
||||
Most messages sent to router actors will be forwarded according to the routers' routing logic.
|
||||
However there are a few types of messages that have special behavior.
|
||||
|
||||
Note that these special messages, except for the ``Broadcast`` message, are only handled by
|
||||
self contained router actors and not by the ``akka.routing.Router`` component described
|
||||
in :ref:`simple-router-java`.
|
||||
|
||||
.. _broadcast-messages-java:
|
||||
|
||||
Broadcast Messages
|
||||
******************
|
||||
------------------
|
||||
|
||||
A ``Broadcast`` message can be used to send a message to *all* of a router's routees. When a router
|
||||
receives a ``Broadcast`` message, it will broadcast that message's *payload* to all routees, no
|
||||
|
|
@ -364,20 +428,20 @@ matter how that router would normally route its messages.
|
|||
The example below shows how you would use a ``Broadcast`` message to send a very important message
|
||||
to every routee of a router.
|
||||
|
||||
.. includecode:: code/docs/jrouting/RouterViaProgramDocTest.java#broadcastDavyJonesWarning
|
||||
.. includecode:: code/docs/jrouting/RouterDocTest.java#broadcastDavyJonesWarning
|
||||
|
||||
In this example the router receives the ``Broadcast`` message, extracts its payload
|
||||
(``"Watch out for Davy Jones' locker"``), and then sends the payload on to all of the router's
|
||||
routees. It is up to each each routee actor to handle the received payload message.
|
||||
|
||||
PoisonPill Messages
|
||||
*******************
|
||||
-------------------
|
||||
|
||||
A ``PoisonPill`` message has special handling for all actors, including for routers. When any actor
|
||||
receives a ``PoisonPill`` message, that actor will be stopped. See the :ref:`poison-pill-java`
|
||||
documentation for details.
|
||||
|
||||
.. includecode:: code/docs/jrouting/RouterViaProgramDocTest.java#poisonPill
|
||||
.. includecode:: code/docs/jrouting/RouterDocTest.java#poisonPill
|
||||
|
||||
For a router, which normally passes on messages to routees, it is important to realise that
|
||||
``PoisonPill`` messages are processed by the router only. ``PoisonPill`` messages sent to a router
|
||||
|
|
@ -386,16 +450,16 @@ will *not* be sent on to routees.
|
|||
However, a ``PoisonPill`` message sent to a router may still affect its routees, because it will
|
||||
stop the router and when the router stops it also stops its children. Stopping children is normal
|
||||
actor behavior. The router will stop routees that it has created as children. Each child will
|
||||
process its current message and then tstop. This may lead to some messages being unprocessed. See
|
||||
the documentation on :ref:`stopping-actors-java` for more information.
|
||||
process its current message and then stop. This may lead to some messages being unprocessed.
|
||||
See the documentation on :ref:`stopping-actors-java` for more information.
|
||||
|
||||
If you wish to stop a router and its routees, but you would like the routees to first process all
|
||||
the messages currently in their mailboxes, then you should not send a ``PoisonPill`` message to the
|
||||
router. Instead you should wrap a ``PoisonPill`` message inside a broadcast message so that each
|
||||
routee will the ``PoisonPill`` message directly. Note that this will stop all routees, even if the
|
||||
router. Instead you should wrap a ``PoisonPill`` message inside a ``Broadcast`` message so that each
|
||||
routee will receive the ``PoisonPill`` message. Note that this will stop all routees, even if the
|
||||
routees aren't children of the router, i.e. even routees programmatically provided to the router.
|
||||
|
||||
.. includecode:: code/docs/jrouting/RouterViaProgramDocTest.java#broadcastPoisonPill
|
||||
.. includecode:: code/docs/jrouting/RouterDocTest.java#broadcastPoisonPill
|
||||
|
||||
With the code shown above, each routee will receive a ``PoisonPill`` message. Each routee will
|
||||
continue to process its messages as normal, eventually processing the ``PoisonPill``. This will
|
||||
|
|
@ -410,48 +474,63 @@ a resizer.
|
|||
discusses in more detail how ``PoisonPill`` messages can be used to shut down routers and routees.
|
||||
|
||||
Kill Messages
|
||||
*************
|
||||
-------------
|
||||
|
||||
``Kill`` messages are another type of message that has special handling. See
|
||||
:ref:`killing-actors-java` for general information about how actors handle ``Kill`` messages.
|
||||
|
||||
When a ``Kill`` message is sent to a router the router processes the message internally, and does
|
||||
*not* send it on to its routees. The router will throw an :class:`ActorKilledException` and fail. It
|
||||
*not* send it on to its routees. The router will throw an ``ActorKilledException`` and fail. It
|
||||
will then be either resumed, restarted or terminated, depending how it is supervised.
|
||||
|
||||
Routees that are children of the router will also be suspended, and will be affected by the
|
||||
supervision directive that is applied to the router. Routees that are not the routers children, i.e.
|
||||
those that were created externally to the router, will not be affected.
|
||||
|
||||
.. includecode:: code/docs/jrouting/RouterViaProgramDocTest.java#kill
|
||||
.. includecode:: code/docs/jrouting/RouterDocTest.java#kill
|
||||
|
||||
As with the ``PoisonPill`` message, there is a distinction between killing a router, which
|
||||
indirectly kills its children (who happen to be routees), and killing routees directly (some of whom
|
||||
may not be children.) To kill routees directly the router should be sent a ``Kill`` message wrapped
|
||||
in a ``Broadcast`` message.
|
||||
|
||||
.. includecode:: code/docs/jrouting/RouterViaProgramDocTest.java#broadcastKill
|
||||
.. includecode:: code/docs/jrouting/RouterDocTest.java#broadcastKill
|
||||
|
||||
Managagement Messages
|
||||
---------------------
|
||||
|
||||
* Sending ``akka.routing.GetRoutees`` to a router actor will make it send back its currently used routees
|
||||
in a ``akka.routing.Routees`` message.
|
||||
* Sending ``akka.routing.AddRoutee`` to a router actor will add that routee to its collection of routees.
|
||||
* Sending ``akka.routing.RemoveRoutee`` to a router actor will remove that routee to its collection of routees.
|
||||
* Sending ``akka.routing.AdjustPoolSize`` to a pool router actor will add or remove that number of routees to
|
||||
its collection of routees.
|
||||
|
||||
These management messages may be handled after other messages, so if you send ``AddRoutee`` immediately followed
|
||||
an ordinary message you are not guaranteed that the routees have been changed when the ordinary message
|
||||
is routed. If you need to know when the change has been applied you can send ``AddRoutee`` followed by ``GetRoutees``
|
||||
and when you receive the ``Routees`` reply you know that the preceeding change has been applied.
|
||||
|
||||
.. _resizable-routers-java:
|
||||
|
||||
Dynamically Resizable Routers
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
Dynamically Resizable Pool
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
All routers can be used with a fixed number of routees or with a resize strategy to adjust the number
|
||||
All pools can be used with a fixed number of routees or with a resize strategy to adjust the number
|
||||
of routees dynamically.
|
||||
|
||||
This is an example of how to create a resizable router that is defined in configuration:
|
||||
Pool with resizer defined in configuration:
|
||||
|
||||
.. includecode:: ../scala/code/docs/routing/RouterViaConfigDocSpec.scala#config-resize
|
||||
.. includecode:: ../scala/code/docs/routing/RouterDocSpec.scala#config-resize-pool
|
||||
|
||||
.. includecode:: code/docs/jrouting/RouterViaConfigExample.java#configurableRoutingWithResizer
|
||||
.. includecode:: code/docs/jrouting/RouterDocTest.java#resize-pool-1
|
||||
|
||||
Several more configuration options are available and described in ``akka.actor.deployment.default.resizer``
|
||||
section of the reference :ref:`configuration`.
|
||||
|
||||
This is an example of how to programmatically create a resizable router:
|
||||
Pool with resizer defined in code:
|
||||
|
||||
.. includecode:: code/docs/jrouting/RouterViaProgramExample.java#programmaticRoutingWithResizer
|
||||
.. includecode:: code/docs/jrouting/RouterDocTest.java#resize-pool-2
|
||||
|
||||
*It is also worth pointing out that if you define the ``router`` in the configuration file then this value
|
||||
will be used instead of any programmatically sent parameters.*
|
||||
|
|
@ -460,7 +539,7 @@ will be used instead of any programmatically sent parameters.*
|
|||
|
||||
Resizing is triggered by sending messages to the actor pool, but it is not
|
||||
completed synchronously; instead a message is sent to the “head”
|
||||
:class:`Router` to perform the size change. Thus you cannot rely on resizing
|
||||
``RouterActor`` to perform the size change. Thus you cannot rely on resizing
|
||||
to instantaneously create new workers when all others are busy, because the
|
||||
message just sent will be queued to the mailbox of a busy actor. To remedy
|
||||
this, configure the pool to use a balancing dispatcher, see `Configuring
|
||||
|
|
@ -478,8 +557,8 @@ routees.
|
|||
A normal actor can be used for routing messages, but an actor's single-threaded processing can
|
||||
become a bottleneck. Routers can achieve much higher throughput with an optimization to the usual
|
||||
message-processing pipeline that allows concurrent routing. This is achieved by embedding routers'
|
||||
routing logic directly in their :class:`ActorRef` rather than in the router actor. Messages sent to
|
||||
a router's :class:`ActorRef` can be immediately routed to the routee, bypassing the single-threaded
|
||||
routing logic directly in their ``ActorRef`` rather than in the router actor. Messages sent to
|
||||
a router's ``ActorRef`` can be immediately routed to the routee, bypassing the single-threaded
|
||||
router actor entirely.
|
||||
|
||||
The cost to this is, of course, that the internals of routing code are more complicated than if
|
||||
|
|
@ -503,82 +582,54 @@ lower maximum throughput is acceptable in your application you may wish to stick
|
|||
actors. This section, however, assumes that you wish to get maximum performance and so demonstrates
|
||||
how you can create your own router.
|
||||
|
||||
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.
|
||||
The router created in this example is replicating each message to a few destinations.
|
||||
|
||||
We begin with defining the class:
|
||||
Start with the routing logic:
|
||||
|
||||
.. includecode:: code/docs/jrouting/CustomRouterDocTest.java#crRouter
|
||||
:exclude: crRoute
|
||||
.. includecode:: code/docs/jrouting/CustomRouterDocTest.java#routing-logic
|
||||
|
||||
The next step is to implement the ``createCustomRoute`` method in the class just defined:
|
||||
``select`` will be called for each message and in this example pick a few destinations by round-robin,
|
||||
by reusing the existing ``RoundRobinRoutingLogic`` and wrap the result in a ``SeveralRoutees``
|
||||
instance. ``SeveralRoutees`` will send the message to all of the supplied routues.
|
||||
|
||||
.. includecode:: code/docs/jrouting/CustomRouterDocTest.java#crRoute
|
||||
The implementation of the routing logic must be thread safe, since it might be used outside of actors.
|
||||
|
||||
As you can see above we start off by creating the routees and put them in a collection.
|
||||
A unit test of the routing logic:
|
||||
|
||||
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/docs/jrouting/CustomRouterDocTest.java#unit-test-logic
|
||||
|
||||
.. includecode:: code/docs/jrouting/CustomRouterDocTest.java#crRegisterRoutees
|
||||
You could stop here and use the ``RedundancyRoutingLogic`` with a ``akka.routing.Router``
|
||||
as described in :ref:`simple-router-java`.
|
||||
|
||||
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:
|
||||
Let us continue and make this into a self contained, configurable, router actor.
|
||||
|
||||
.. includecode:: code/docs/jrouting/CustomRouterDocTest.java#crRoutingLogic
|
||||
Create a class that extends ``PoolBase``, ``GroupBase`` or ``CustomRouterConfig``. That class is a factory
|
||||
for the routing logic and holds the configuration for the router. Here we make it a ``Group``.
|
||||
|
||||
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/akka/akka/blob/master/akka-actor/src/main/scala/akka/routing/Routing.scala#L375>`_
|
||||
.. includecode:: code/docs/jrouting/RedundancyGroup.java#group
|
||||
|
||||
All in all the custom router looks like this:
|
||||
This can be used exactly as the router actors provided by Akka.
|
||||
|
||||
.. includecode:: code/docs/jrouting/CustomRouterDocTest.java#CustomRouter
|
||||
.. includecode:: code/docs/jrouting/CustomRouterDocTest.java#usage-1
|
||||
|
||||
If you are interested in how to use the VoteCountRouter it looks like this:
|
||||
Note that we added a constructor in ``RedundancyGroup`` that takes a ``Config`` parameter.
|
||||
That makes it possible to define it in configuration.
|
||||
|
||||
.. includecode:: code/docs/jrouting/CustomRouterDocTest.java#crTest
|
||||
.. includecode:: ../scala/code/docs/routing/CustomRouterDocSpec.scala#jconfig
|
||||
|
||||
.. caution::
|
||||
|
||||
When creating a cutom router the resulting RoutedActorRef optimizes the
|
||||
sending of the message so that it does NOT go through the router’s mailbox
|
||||
unless the route returns an empty recipient set.
|
||||
|
||||
This means that the ``route`` function defined in the ``RouterConfig``
|
||||
or the function returned from ``CreateCustomRoute`` in
|
||||
``CustomRouterConfig`` is evaluated concurrently without protection by
|
||||
the RoutedActorRef: either provide a reentrant (i.e. pure) implementation
|
||||
or do the locking yourself!
|
||||
|
||||
|
||||
Configured Custom Router
|
||||
************************
|
||||
|
||||
It is possible to define configuration properties for custom routers. In the ``router`` property of the deployment
|
||||
configuration you define the fully qualified class name of the router class. The router class must extend
|
||||
``akka.routing.CustomRouterConfig`` and have constructor with one ``com.typesafe.config.Config`` parameter.
|
||||
Note the fully qualified class name in the ``router`` property. The router class must extend
|
||||
``akka.routing.RouterConfig`` (``Pool``, ``Group`` or ``CustomRouterConfig``) and have
|
||||
constructor with one ``com.typesafe.config.Config`` parameter.
|
||||
The deployment section of the configuration is passed to the constructor.
|
||||
|
||||
Custom Resizer
|
||||
**************
|
||||
|
||||
A router with dynamically resizable number of routees is implemented by providing a ``akka.routing.Resizer``
|
||||
in ``resizer`` method of the ``RouterConfig``. See ``akka.routing.DefaultResizer`` for inspiration
|
||||
of how to write your own resize strategy.
|
||||
|
||||
.. includecode:: code/docs/jrouting/CustomRouterDocTest.java#usage-2
|
||||
|
||||
Configuring Dispatchers
|
||||
^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
The dispatcher for created children of the router will be taken from
|
||||
:class:`Props` as described in :ref:`dispatchers-java`. For a dynamic pool it
|
||||
makes sense to configure the :class:`BalancingDispatcher` if the precise
|
||||
``Props`` as described in :ref:`dispatchers-java`. For a pool it
|
||||
makes sense to configure the ``BalancingDispatcher`` if the precise
|
||||
routing is not so important (i.e. no consistent hashing or round-robin is
|
||||
required); this enables newly created routees to pick up work immediately by
|
||||
stealing it from their siblings.
|
||||
|
|
@ -591,13 +642,13 @@ stealing it from their siblings.
|
|||
|
||||
The “head” router cannot always run on the same dispatcher, because it
|
||||
does not process the same type of messages, hence this special actor does
|
||||
not use the dispatcher configured in :class:`Props`, but takes the
|
||||
not use the dispatcher configured in ``Props``, but takes the
|
||||
``routerDispatcher`` from the :class:`RouterConfig` instead, which defaults to
|
||||
the actor system’s default dispatcher. All standard routers allow setting this
|
||||
property in their constructor or factory method, custom routers have to
|
||||
implement the method in a suitable way.
|
||||
|
||||
.. includecode:: code/docs/jrouting/CustomRouterDocTest.java#dispatchers
|
||||
.. includecode:: code/docs/jrouting/RouterDocTest.java#dispatchers
|
||||
|
||||
.. note::
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue