!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:
Patrik Nordwall 2013-09-19 08:00:05 +02:00
parent 81ca6fe8c8
commit ebadd567b2
104 changed files with 9671 additions and 5006 deletions

View file

@ -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
}