2013-11-19 15:53:40 +01:00
|
|
|
/**
|
2016-02-23 12:58:39 +01:00
|
|
|
* Copyright (C) 2009-2016 Lightbend Inc. <http://www.lightbend.com>
|
2013-11-19 15:53:40 +01:00
|
|
|
*/
|
|
|
|
|
|
2015-04-27 14:48:28 +02:00
|
|
|
package akka.cluster.sharding;
|
2013-11-19 15:53:40 +01:00
|
|
|
|
2015-12-18 11:35:40 +01:00
|
|
|
import static java.util.concurrent.TimeUnit.SECONDS;
|
2013-11-19 15:53:40 +01:00
|
|
|
import scala.concurrent.duration.Duration;
|
2015-02-17 20:17:06 +01:00
|
|
|
|
|
|
|
|
import akka.actor.AbstractActor;
|
2015-09-10 15:35:26 +02:00
|
|
|
import akka.actor.ActorInitializationException;
|
2013-11-19 15:53:40 +01:00
|
|
|
import akka.actor.ActorRef;
|
|
|
|
|
import akka.actor.ActorSystem;
|
2015-09-10 15:35:26 +02:00
|
|
|
import akka.actor.OneForOneStrategy;
|
2013-11-19 15:53:40 +01:00
|
|
|
import akka.actor.PoisonPill;
|
|
|
|
|
import akka.actor.Props;
|
2015-09-10 15:35:26 +02:00
|
|
|
import akka.actor.SupervisorStrategy;
|
2015-02-17 20:17:06 +01:00
|
|
|
import akka.actor.Terminated;
|
2013-11-19 15:53:40 +01:00
|
|
|
import akka.actor.ReceiveTimeout;
|
2015-09-10 15:35:26 +02:00
|
|
|
import akka.actor.UntypedActor;
|
2013-11-19 15:53:40 +01:00
|
|
|
import akka.japi.Procedure;
|
2014-10-24 12:59:58 -04:00
|
|
|
import akka.japi.Option;
|
2014-05-21 01:35:21 +02:00
|
|
|
import akka.persistence.UntypedPersistentActor;
|
2015-02-17 20:17:06 +01:00
|
|
|
import akka.cluster.Cluster;
|
2015-09-10 15:35:26 +02:00
|
|
|
import akka.japi.pf.DeciderBuilder;
|
2015-02-17 20:17:06 +01:00
|
|
|
import akka.japi.pf.ReceiveBuilder;
|
2013-11-19 15:53:40 +01:00
|
|
|
|
|
|
|
|
// Doc code, compile only
|
|
|
|
|
public class ClusterShardingTest {
|
|
|
|
|
|
|
|
|
|
ActorSystem system = null;
|
|
|
|
|
|
|
|
|
|
ActorRef getSelf() {
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void demonstrateUsage() {
|
|
|
|
|
//#counter-extractor
|
|
|
|
|
ShardRegion.MessageExtractor messageExtractor = new ShardRegion.MessageExtractor() {
|
|
|
|
|
|
|
|
|
|
@Override
|
2015-06-09 12:25:58 +02:00
|
|
|
public String entityId(Object message) {
|
|
|
|
|
if (message instanceof Counter.EntityEnvelope)
|
|
|
|
|
return String.valueOf(((Counter.EntityEnvelope) message).id);
|
2013-11-19 15:53:40 +01:00
|
|
|
else if (message instanceof Counter.Get)
|
|
|
|
|
return String.valueOf(((Counter.Get) message).counterId);
|
|
|
|
|
else
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Override
|
2015-06-09 12:25:58 +02:00
|
|
|
public Object entityMessage(Object message) {
|
|
|
|
|
if (message instanceof Counter.EntityEnvelope)
|
|
|
|
|
return ((Counter.EntityEnvelope) message).payload;
|
2013-11-19 15:53:40 +01:00
|
|
|
else
|
|
|
|
|
return message;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Override
|
|
|
|
|
public String shardId(Object message) {
|
2014-10-27 10:52:34 +01:00
|
|
|
int numberOfShards = 100;
|
2015-06-09 12:25:58 +02:00
|
|
|
if (message instanceof Counter.EntityEnvelope) {
|
|
|
|
|
long id = ((Counter.EntityEnvelope) message).id;
|
2014-10-27 10:52:34 +01:00
|
|
|
return String.valueOf(id % numberOfShards);
|
2013-11-19 15:53:40 +01:00
|
|
|
} else if (message instanceof Counter.Get) {
|
|
|
|
|
long id = ((Counter.Get) message).counterId;
|
2014-10-27 10:52:34 +01:00
|
|
|
return String.valueOf(id % numberOfShards);
|
2013-11-19 15:53:40 +01:00
|
|
|
} else {
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
};
|
|
|
|
|
//#counter-extractor
|
|
|
|
|
|
|
|
|
|
//#counter-start
|
2014-10-24 12:59:58 -04:00
|
|
|
Option<String> roleOption = Option.none();
|
2015-06-07 14:49:38 +02:00
|
|
|
ClusterShardingSettings settings = ClusterShardingSettings.create(system);
|
2015-02-17 20:17:06 +01:00
|
|
|
ActorRef startedCounterRegion = ClusterSharding.get(system).start("Counter",
|
2015-06-07 14:49:38 +02:00
|
|
|
Props.create(Counter.class), settings, messageExtractor);
|
2013-11-19 15:53:40 +01:00
|
|
|
//#counter-start
|
|
|
|
|
|
|
|
|
|
//#counter-usage
|
|
|
|
|
ActorRef counterRegion = ClusterSharding.get(system).shardRegion("Counter");
|
2014-10-27 10:52:34 +01:00
|
|
|
counterRegion.tell(new Counter.Get(123), getSelf());
|
2013-11-19 15:53:40 +01:00
|
|
|
|
2015-06-09 12:25:58 +02:00
|
|
|
counterRegion.tell(new Counter.EntityEnvelope(123,
|
2013-11-19 15:53:40 +01:00
|
|
|
Counter.CounterOp.INCREMENT), getSelf());
|
2014-10-27 10:52:34 +01:00
|
|
|
counterRegion.tell(new Counter.Get(123), getSelf());
|
2013-11-19 15:53:40 +01:00
|
|
|
//#counter-usage
|
2015-09-10 15:35:26 +02:00
|
|
|
|
|
|
|
|
//#counter-supervisor-start
|
|
|
|
|
ClusterSharding.get(system).start("SupervisedCounter",
|
|
|
|
|
Props.create(CounterSupervisor.class), settings, messageExtractor);
|
|
|
|
|
//#counter-supervisor-start
|
2013-11-19 15:53:40 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static//#counter-actor
|
2014-05-21 01:35:21 +02:00
|
|
|
public class Counter extends UntypedPersistentActor {
|
2013-11-19 15:53:40 +01:00
|
|
|
|
|
|
|
|
public static enum CounterOp {
|
|
|
|
|
INCREMENT, DECREMENT
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public static class Get {
|
|
|
|
|
final public long counterId;
|
|
|
|
|
|
|
|
|
|
public Get(long counterId) {
|
|
|
|
|
this.counterId = counterId;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2015-06-09 12:25:58 +02:00
|
|
|
public static class EntityEnvelope {
|
2013-11-19 15:53:40 +01:00
|
|
|
final public long id;
|
|
|
|
|
final public Object payload;
|
|
|
|
|
|
2015-06-09 12:25:58 +02:00
|
|
|
public EntityEnvelope(long id, Object payload) {
|
2013-11-19 15:53:40 +01:00
|
|
|
this.id = id;
|
|
|
|
|
this.payload = payload;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public static class CounterChanged {
|
|
|
|
|
final public int delta;
|
|
|
|
|
|
|
|
|
|
public CounterChanged(int delta) {
|
|
|
|
|
this.delta = delta;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int count = 0;
|
2014-07-08 17:51:18 +01:00
|
|
|
|
2015-06-09 12:25:58 +02:00
|
|
|
// getSelf().path().name() is the entity identifier (utf-8 URL-encoded)
|
2014-06-26 13:56:01 +02:00
|
|
|
@Override
|
|
|
|
|
public String persistenceId() {
|
2015-06-30 11:43:37 +02:00
|
|
|
return "Counter-" + getSelf().path().name();
|
2014-06-26 13:56:01 +02:00
|
|
|
}
|
2013-11-19 15:53:40 +01:00
|
|
|
|
|
|
|
|
@Override
|
|
|
|
|
public void preStart() throws Exception {
|
|
|
|
|
super.preStart();
|
2015-12-18 11:35:40 +01:00
|
|
|
context().setReceiveTimeout(Duration.create(120, SECONDS));
|
2013-11-19 15:53:40 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void updateState(CounterChanged event) {
|
|
|
|
|
count += event.delta;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Override
|
2014-01-19 17:46:32 +01:00
|
|
|
public void onReceiveRecover(Object msg) {
|
2013-11-19 15:53:40 +01:00
|
|
|
if (msg instanceof CounterChanged)
|
|
|
|
|
updateState((CounterChanged) msg);
|
|
|
|
|
else
|
|
|
|
|
unhandled(msg);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Override
|
|
|
|
|
public void onReceiveCommand(Object msg) {
|
|
|
|
|
if (msg instanceof Get)
|
|
|
|
|
getSender().tell(count, getSelf());
|
|
|
|
|
|
|
|
|
|
else if (msg == CounterOp.INCREMENT)
|
|
|
|
|
persist(new CounterChanged(+1), new Procedure<CounterChanged>() {
|
|
|
|
|
public void apply(CounterChanged evt) {
|
|
|
|
|
updateState(evt);
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
else if (msg == CounterOp.DECREMENT)
|
|
|
|
|
persist(new CounterChanged(-1), new Procedure<CounterChanged>() {
|
|
|
|
|
public void apply(CounterChanged evt) {
|
|
|
|
|
updateState(evt);
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
else if (msg.equals(ReceiveTimeout.getInstance()))
|
|
|
|
|
getContext().parent().tell(
|
|
|
|
|
new ShardRegion.Passivate(PoisonPill.getInstance()), getSelf());
|
|
|
|
|
|
|
|
|
|
else
|
|
|
|
|
unhandled(msg);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//#counter-actor
|
|
|
|
|
|
2015-02-17 20:17:06 +01:00
|
|
|
static//#graceful-shutdown
|
|
|
|
|
public class IllustrateGracefulShutdown extends AbstractActor {
|
|
|
|
|
|
|
|
|
|
public IllustrateGracefulShutdown() {
|
|
|
|
|
final ActorSystem system = context().system();
|
|
|
|
|
final Cluster cluster = Cluster.get(system);
|
|
|
|
|
final ActorRef region = ClusterSharding.get(system).shardRegion("Entity");
|
|
|
|
|
|
|
|
|
|
receive(ReceiveBuilder.
|
|
|
|
|
match(String.class, s -> s.equals("leave"), s -> {
|
|
|
|
|
context().watch(region);
|
|
|
|
|
region.tell(ShardRegion.gracefulShutdownInstance(), self());
|
|
|
|
|
}).
|
|
|
|
|
match(Terminated.class, t -> t.actor().equals(region), t -> {
|
2015-12-18 11:35:40 +01:00
|
|
|
cluster.registerOnMemberRemoved(() ->
|
|
|
|
|
self().tell("member-removed", self()));
|
2015-02-17 20:17:06 +01:00
|
|
|
cluster.leave(cluster.selfAddress());
|
2015-12-18 11:35:40 +01:00
|
|
|
}).
|
|
|
|
|
match(String.class, s -> s.equals("member-removed"), s -> {
|
|
|
|
|
// Let singletons hand over gracefully before stopping the system
|
2015-12-21 09:54:14 +01:00
|
|
|
context().system().scheduler().scheduleOnce(Duration.create(10, SECONDS),
|
2015-12-18 11:35:40 +01:00
|
|
|
self(), "stop-system", context().dispatcher(), self());
|
|
|
|
|
}).
|
|
|
|
|
match(String.class, s -> s.equals("stop-system"), s -> {
|
|
|
|
|
system.terminate();
|
|
|
|
|
}).
|
|
|
|
|
build());
|
2015-02-17 20:17:06 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
//#graceful-shutdown
|
|
|
|
|
|
2015-09-10 15:35:26 +02:00
|
|
|
static//#supervisor
|
|
|
|
|
public class CounterSupervisor extends UntypedActor {
|
|
|
|
|
|
|
|
|
|
private final ActorRef counter = getContext().actorOf(
|
|
|
|
|
Props.create(Counter.class), "theCounter");
|
|
|
|
|
|
|
|
|
|
private static final SupervisorStrategy strategy =
|
|
|
|
|
new OneForOneStrategy(DeciderBuilder.
|
|
|
|
|
match(IllegalArgumentException.class, e -> SupervisorStrategy.resume()).
|
|
|
|
|
match(ActorInitializationException.class, e -> SupervisorStrategy.stop()).
|
|
|
|
|
match(Exception.class, e -> SupervisorStrategy.restart()).
|
|
|
|
|
matchAny(o -> SupervisorStrategy.escalate()).build());
|
|
|
|
|
|
|
|
|
|
@Override
|
|
|
|
|
public SupervisorStrategy supervisorStrategy() {
|
|
|
|
|
return strategy;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Override
|
|
|
|
|
public void onReceive(Object msg) {
|
|
|
|
|
counter.forward(msg, getContext());
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
//#supervisor
|
|
|
|
|
|
2013-11-19 15:53:40 +01:00
|
|
|
}
|