Remove samples (#22288)

Add code, that was used for documentation to the appropriate projects
or akka-docs.
This commit is contained in:
Martynas Mickevičius 2017-02-14 13:10:23 +02:00 committed by Johan Andrén
parent b8cdcf3439
commit 958de6a916
373 changed files with 1201 additions and 17259 deletions

View file

@ -174,7 +174,7 @@ public class FSMDocTest extends AbstractJavaTest {
expectMsgEquals(Active);
expectMsgEquals(Data.Foo);
String msg = expectMsgClass(String.class);
assertThat(msg, CoreMatchers.startsWith("LogEntry(SomeState,Foo,Actor[akka://FSMDocTest/system/"));
assertTrue(msg.startsWith("LogEntry(SomeState,Foo,Actor[akka://FSMDocTest/system/"));
}};
}
}

View file

@ -21,7 +21,7 @@ public class ClusterDocTest extends AbstractJavaTest {
@BeforeClass
public static void setup() {
system = ActorSystem.create("ClusterDocTest",
ConfigFactory.parseString(ClusterDocSpec.config()));
ConfigFactory.parseString(scala.docs.cluster.ClusterDocSpec.config()));
}
@AfterClass

View file

@ -0,0 +1,47 @@
package docs.cluster;
import java.math.BigInteger;
import java.util.concurrent.Callable;
import scala.concurrent.Future;
import akka.actor.UntypedActor;
import akka.dispatch.Mapper;
import static akka.dispatch.Futures.future;
import static akka.pattern.Patterns.pipe;
//#backend
public class FactorialBackend extends UntypedActor {
@Override
public void onReceive(Object message) {
if (message instanceof Integer) {
final Integer n = (Integer) message;
Future<BigInteger> f = future(new Callable<BigInteger>() {
public BigInteger call() {
return factorial(n);
}
}, getContext().dispatcher());
Future<FactorialResult> result = f.map(
new Mapper<BigInteger, FactorialResult>() {
public FactorialResult apply(BigInteger factorial) {
return new FactorialResult(n, factorial);
}
}, getContext().dispatcher());
pipe(result, getContext().dispatcher()).to(getSender());
} else {
unhandled(message);
}
}
BigInteger factorial(int n) {
BigInteger acc = BigInteger.ONE;
for (int i = 1; i <= n; ++i) {
acc = acc.multiply(BigInteger.valueOf(i));
}
return acc;
}
}
//#backend

View file

@ -0,0 +1,107 @@
package docs.cluster;
import java.util.Arrays;
import java.util.Collections;
import java.util.concurrent.TimeUnit;
import akka.actor.Props;
import akka.cluster.metrics.AdaptiveLoadBalancingGroup;
import akka.cluster.metrics.AdaptiveLoadBalancingPool;
import akka.cluster.metrics.HeapMetricsSelector;
import akka.cluster.metrics.SystemLoadAverageMetricsSelector;
import akka.cluster.routing.ClusterRouterGroup;
import akka.cluster.routing.ClusterRouterGroupSettings;
import akka.cluster.routing.ClusterRouterPool;
import akka.cluster.routing.ClusterRouterPoolSettings;
import akka.routing.ConsistentHashingGroup;
import akka.routing.ConsistentHashingPool;
import scala.concurrent.duration.Duration;
import akka.actor.ActorRef;
import akka.actor.ReceiveTimeout;
import akka.actor.UntypedActor;
import akka.event.Logging;
import akka.event.LoggingAdapter;
import akka.routing.FromConfig;
//#frontend
public class FactorialFrontend extends UntypedActor {
final int upToN;
final boolean repeat;
LoggingAdapter log = Logging.getLogger(getContext().system(), this);
ActorRef backend = getContext().actorOf(FromConfig.getInstance().props(),
"factorialBackendRouter");
public FactorialFrontend(int upToN, boolean repeat) {
this.upToN = upToN;
this.repeat = repeat;
}
@Override
public void preStart() {
sendJobs();
getContext().setReceiveTimeout(Duration.create(10, TimeUnit.SECONDS));
}
@Override
public void onReceive(Object message) {
if (message instanceof FactorialResult) {
FactorialResult result = (FactorialResult) message;
if (result.n == upToN) {
log.debug("{}! = {}", result.n, result.factorial);
if (repeat)
sendJobs();
else
getContext().stop(getSelf());
}
} else if (message instanceof ReceiveTimeout) {
log.info("Timeout");
sendJobs();
} else {
unhandled(message);
}
}
void sendJobs() {
log.info("Starting batch of factorials up to [{}]", upToN);
for (int n = 1; n <= upToN; n++) {
backend.tell(n, getSelf());
}
}
}
//#frontend
//not used, only for documentation
abstract class FactorialFrontend2 extends UntypedActor {
//#router-lookup-in-code
int totalInstances = 100;
Iterable<String> routeesPaths = Arrays.asList("/user/factorialBackend", "");
boolean allowLocalRoutees = true;
String useRole = "backend";
ActorRef backend = getContext().actorOf(
new ClusterRouterGroup(new AdaptiveLoadBalancingGroup(
HeapMetricsSelector.getInstance(), Collections.<String> emptyList()),
new ClusterRouterGroupSettings(totalInstances, routeesPaths,
allowLocalRoutees, useRole)).props(), "factorialBackendRouter2");
//#router-lookup-in-code
}
//not used, only for documentation
abstract class FactorialFrontend3 extends UntypedActor {
//#router-deploy-in-code
int totalInstances = 100;
int maxInstancesPerNode = 3;
boolean allowLocalRoutees = false;
String useRole = "backend";
ActorRef backend = getContext().actorOf(
new ClusterRouterPool(new AdaptiveLoadBalancingPool(
SystemLoadAverageMetricsSelector.getInstance(), 0),
new ClusterRouterPoolSettings(totalInstances, maxInstancesPerNode,
allowLocalRoutees, useRole)).props(Props
.create(FactorialBackend.class)), "factorialBackendRouter3");
//#router-deploy-in-code
}

View file

@ -0,0 +1,72 @@
package docs.cluster;
import java.util.concurrent.TimeoutException;
import java.util.concurrent.TimeUnit;
import scala.concurrent.Await;
import scala.concurrent.duration.Duration;
import com.typesafe.config.Config;
import com.typesafe.config.ConfigFactory;
import akka.actor.ActorSystem;
import akka.actor.Props;
import akka.cluster.Cluster;
public class FactorialFrontendMain {
public static void main(String[] args) {
final int upToN = 200;
final Config config = ConfigFactory.parseString(
"akka.cluster.roles = [frontend]").withFallback(
ConfigFactory.load("factorial"));
final ActorSystem system = ActorSystem.create("ClusterSystem", config);
system.log().info(
"Factorials will start when 2 backend members in the cluster.");
//#registerOnUp
Cluster.get(system).registerOnMemberUp(new Runnable() {
@Override
public void run() {
system.actorOf(Props.create(FactorialFrontend.class, upToN, true),
"factorialFrontend");
}
});
//#registerOnUp
//#registerOnRemoved
Cluster.get(system).registerOnMemberRemoved(new Runnable() {
@Override
public void run() {
// exit JVM when ActorSystem has been terminated
final Runnable exit = new Runnable() {
@Override public void run() {
System.exit(0);
}
};
system.registerOnTermination(exit);
// shut down ActorSystem
system.terminate();
// In case ActorSystem shutdown takes longer than 10 seconds,
// exit the JVM forcefully anyway.
// We must spawn a separate thread to not block current thread,
// since that would have blocked the shutdown of the ActorSystem.
new Thread() {
@Override public void run(){
try {
Await.ready(system.whenTerminated(), Duration.create(10, TimeUnit.SECONDS));
} catch (Exception e) {
System.exit(-1);
}
}
}.start();
}
});
//#registerOnRemoved
}
}

View file

@ -0,0 +1,15 @@
package docs.cluster;
import java.math.BigInteger;
import java.io.Serializable;
public class FactorialResult implements Serializable {
private static final long serialVersionUID = 1L;
public final int n;
public final BigInteger factorial;
FactorialResult(int n, BigInteger factorial) {
this.n = n;
this.factorial = factorial;
}
}

View file

@ -0,0 +1,71 @@
package docs.cluster;
//#metrics-listener
import akka.actor.UntypedActor;
import akka.cluster.Cluster;
import akka.cluster.ClusterEvent.CurrentClusterState;
import akka.cluster.metrics.ClusterMetricsChanged;
import akka.cluster.metrics.NodeMetrics;
import akka.cluster.metrics.StandardMetrics;
import akka.cluster.metrics.StandardMetrics.HeapMemory;
import akka.cluster.metrics.StandardMetrics.Cpu;
import akka.cluster.metrics.ClusterMetricsExtension;
import akka.event.Logging;
import akka.event.LoggingAdapter;
public class MetricsListener extends UntypedActor {
LoggingAdapter log = Logging.getLogger(getContext().system(), this);
Cluster cluster = Cluster.get(getContext().system());
ClusterMetricsExtension extension = ClusterMetricsExtension.get(getContext().system());
// Subscribe unto ClusterMetricsEvent events.
@Override
public void preStart() {
extension.subscribe(getSelf());
}
// Unsubscribe from ClusterMetricsEvent events.
@Override
public void postStop() {
extension.unsubscribe(getSelf());
}
@Override
public void onReceive(Object message) {
if (message instanceof ClusterMetricsChanged) {
ClusterMetricsChanged clusterMetrics = (ClusterMetricsChanged) message;
for (NodeMetrics nodeMetrics : clusterMetrics.getNodeMetrics()) {
if (nodeMetrics.address().equals(cluster.selfAddress())) {
logHeap(nodeMetrics);
logCpu(nodeMetrics);
}
}
} else if (message instanceof CurrentClusterState) {
// Ignore.
} else {
unhandled(message);
}
}
void logHeap(NodeMetrics nodeMetrics) {
HeapMemory heap = StandardMetrics.extractHeapMemory(nodeMetrics);
if (heap != null) {
log.info("Used heap: {} MB", ((double) heap.used()) / 1024 / 1024);
}
}
void logCpu(NodeMetrics nodeMetrics) {
Cpu cpu = StandardMetrics.extractCpu(nodeMetrics);
if (cpu != null && cpu.systemLoadAverage().isDefined()) {
log.info("Load: {} ({} processors)", cpu.systemLoadAverage().get(),
cpu.processors());
}
}
}
//#metrics-listener

View file

@ -0,0 +1,54 @@
package docs.cluster;
import akka.actor.UntypedActor;
import akka.cluster.Cluster;
import akka.cluster.ClusterEvent;
import akka.cluster.ClusterEvent.MemberEvent;
import akka.cluster.ClusterEvent.MemberUp;
import akka.cluster.ClusterEvent.MemberRemoved;
import akka.cluster.ClusterEvent.UnreachableMember;
import akka.event.Logging;
import akka.event.LoggingAdapter;
public class SimpleClusterListener extends UntypedActor {
LoggingAdapter log = Logging.getLogger(getContext().system(), this);
Cluster cluster = Cluster.get(getContext().system());
//subscribe to cluster changes
@Override
public void preStart() {
//#subscribe
cluster.subscribe(getSelf(), ClusterEvent.initialStateAsEvents(),
MemberEvent.class, UnreachableMember.class);
//#subscribe
}
//re-subscribe when restart
@Override
public void postStop() {
cluster.unsubscribe(getSelf());
}
@Override
public void onReceive(Object message) {
if (message instanceof MemberUp) {
MemberUp mUp = (MemberUp) message;
log.info("Member is Up: {}", mUp.member());
} else if (message instanceof UnreachableMember) {
UnreachableMember mUnreachable = (UnreachableMember) message;
log.info("Member detected as unreachable: {}", mUnreachable.member());
} else if (message instanceof MemberRemoved) {
MemberRemoved mRemoved = (MemberRemoved) message;
log.info("Member is Removed: {}", mRemoved.member());
} else if (message instanceof MemberEvent) {
// ignore
} else {
unhandled(message);
}
}
}

View file

@ -0,0 +1,57 @@
package docs.cluster;
import akka.actor.UntypedActor;
import akka.cluster.Cluster;
import akka.cluster.ClusterEvent.CurrentClusterState;
import akka.cluster.ClusterEvent.MemberEvent;
import akka.cluster.ClusterEvent.MemberUp;
import akka.cluster.ClusterEvent.MemberRemoved;
import akka.cluster.ClusterEvent.UnreachableMember;
import akka.event.Logging;
import akka.event.LoggingAdapter;
public class SimpleClusterListener2 extends UntypedActor {
LoggingAdapter log = Logging.getLogger(getContext().system(), this);
Cluster cluster = Cluster.get(getContext().system());
//subscribe to cluster changes
@Override
public void preStart() {
//#subscribe
cluster.subscribe(getSelf(), MemberEvent.class, UnreachableMember.class);
//#subscribe
}
//re-subscribe when restart
@Override
public void postStop() {
cluster.unsubscribe(getSelf());
}
@Override
public void onReceive(Object message) {
if (message instanceof CurrentClusterState) {
CurrentClusterState state = (CurrentClusterState) message;
log.info("Current members: {}", state.members());
} else if (message instanceof MemberUp) {
MemberUp mUp = (MemberUp) message;
log.info("Member is Up: {}", mUp.member());
} else if (message instanceof UnreachableMember) {
UnreachableMember mUnreachable = (UnreachableMember) message;
log.info("Member detected as unreachable: {}", mUnreachable.member());
} else if (message instanceof MemberRemoved) {
MemberRemoved mRemoved = (MemberRemoved) message;
log.info("Member is Removed: {}", mRemoved.member());
} else if (message instanceof MemberEvent) {
// ignore
} else {
unhandled(message);
}
}
}

View file

@ -0,0 +1,56 @@
package docs.cluster;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.TimeUnit;
import docs.cluster.StatsMessages.JobFailed;
import docs.cluster.StatsMessages.StatsResult;
import scala.concurrent.duration.Duration;
import akka.actor.ActorRef;
import akka.actor.ReceiveTimeout;
import akka.actor.UntypedActor;
//#aggregator
public class StatsAggregator extends UntypedActor {
final int expectedResults;
final ActorRef replyTo;
final List<Integer> results = new ArrayList<Integer>();
public StatsAggregator(int expectedResults, ActorRef replyTo) {
this.expectedResults = expectedResults;
this.replyTo = replyTo;
}
@Override
public void preStart() {
getContext().setReceiveTimeout(Duration.create(3, TimeUnit.SECONDS));
}
@Override
public void onReceive(Object message) {
if (message instanceof Integer) {
Integer wordCount = (Integer) message;
results.add(wordCount);
if (results.size() == expectedResults) {
int sum = 0;
for (int c : results)
sum += c;
double meanWordLength = ((double) sum) / results.size();
replyTo.tell(new StatsResult(meanWordLength), getSelf());
getContext().stop(getSelf());
}
} else if (message == ReceiveTimeout.getInstance()) {
replyTo.tell(new JobFailed("Service unavailable, try again later"),
getSelf());
getContext().stop(getSelf());
} else {
unhandled(message);
}
}
}
//#aggregator

View file

@ -0,0 +1,55 @@
package docs.cluster;
import java.io.Serializable;
//#messages
public interface StatsMessages {
public static class StatsJob implements Serializable {
private final String text;
public StatsJob(String text) {
this.text = text;
}
public String getText() {
return text;
}
}
public static class StatsResult implements Serializable {
private final double meanWordLength;
public StatsResult(double meanWordLength) {
this.meanWordLength = meanWordLength;
}
public double getMeanWordLength() {
return meanWordLength;
}
@Override
public String toString() {
return "meanWordLength: " + meanWordLength;
}
}
public static class JobFailed implements Serializable {
private final String reason;
public JobFailed(String reason) {
this.reason = reason;
}
public String getReason() {
return reason;
}
@Override
public String toString() {
return "JobFailed(" + reason + ")";
}
}
}
//#messages

View file

@ -0,0 +1,111 @@
package docs.cluster;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import docs.cluster.StatsMessages.JobFailed;
import docs.cluster.StatsMessages.StatsJob;
import docs.cluster.StatsMessages.StatsResult;
import java.util.concurrent.ThreadLocalRandom;
import scala.concurrent.duration.Duration;
import scala.concurrent.duration.FiniteDuration;
import akka.actor.ActorSelection;
import akka.actor.Address;
import akka.actor.Cancellable;
import akka.actor.UntypedActor;
import akka.cluster.Cluster;
import akka.cluster.ClusterEvent.UnreachableMember;
import akka.cluster.ClusterEvent.ReachableMember;
import akka.cluster.ClusterEvent.CurrentClusterState;
import akka.cluster.ClusterEvent.MemberEvent;
import akka.cluster.ClusterEvent.MemberUp;
import akka.cluster.ClusterEvent.ReachabilityEvent;
import akka.cluster.Member;
import akka.cluster.MemberStatus;
public class StatsSampleClient extends UntypedActor {
final String servicePath;
final Cancellable tickTask;
final Set<Address> nodes = new HashSet<Address>();
Cluster cluster = Cluster.get(getContext().system());
public StatsSampleClient(String servicePath) {
this.servicePath = servicePath;
FiniteDuration interval = Duration.create(2, TimeUnit.SECONDS);
tickTask = getContext()
.system()
.scheduler()
.schedule(interval, interval, getSelf(), "tick",
getContext().dispatcher(), null);
}
//subscribe to cluster changes, MemberEvent
@Override
public void preStart() {
cluster.subscribe(getSelf(), MemberEvent.class, ReachabilityEvent.class);
}
//re-subscribe when restart
@Override
public void postStop() {
cluster.unsubscribe(getSelf());
tickTask.cancel();
}
@Override
public void onReceive(Object message) {
if (message.equals("tick") && !nodes.isEmpty()) {
// just pick any one
List<Address> nodesList = new ArrayList<Address>(nodes);
Address address = nodesList.get(ThreadLocalRandom.current().nextInt(
nodesList.size()));
ActorSelection service = getContext().actorSelection(address + servicePath);
service.tell(new StatsJob("this is the text that will be analyzed"),
getSelf());
} else if (message instanceof StatsResult) {
StatsResult result = (StatsResult) message;
System.out.println(result);
} else if (message instanceof JobFailed) {
JobFailed failed = (JobFailed) message;
System.out.println(failed);
} else if (message instanceof CurrentClusterState) {
CurrentClusterState state = (CurrentClusterState) message;
nodes.clear();
for (Member member : state.getMembers()) {
if (member.hasRole("compute") && member.status().equals(MemberStatus.up())) {
nodes.add(member.address());
}
}
} else if (message instanceof MemberUp) {
MemberUp mUp = (MemberUp) message;
if (mUp.member().hasRole("compute"))
nodes.add(mUp.member().address());
} else if (message instanceof MemberEvent) {
MemberEvent other = (MemberEvent) message;
nodes.remove(other.member().address());
} else if (message instanceof UnreachableMember) {
UnreachableMember unreachable = (UnreachableMember) message;
nodes.remove(unreachable.member().address());
} else if (message instanceof ReachableMember) {
ReachableMember reachable = (ReachableMember) message;
if (reachable.member().hasRole("compute"))
nodes.add(reachable.member().address());
} else {
unhandled(message);
}
}
}

View file

@ -0,0 +1,19 @@
package docs.cluster;
import com.typesafe.config.ConfigFactory;
import akka.actor.ActorSystem;
import akka.actor.Props;
public class StatsSampleOneMasterClientMain {
public static void main(String[] args) {
// note that client is not a compute node, role not defined
ActorSystem system = ActorSystem.create("ClusterSystem",
ConfigFactory.load("stats2"));
system.actorOf(Props.create(StatsSampleClient.class, "/user/statsServiceProxy"),
"client");
}
}

View file

@ -0,0 +1,53 @@
package docs.cluster;
import com.typesafe.config.Config;
import com.typesafe.config.ConfigFactory;
import akka.actor.ActorSystem;
import akka.actor.PoisonPill;
import akka.actor.Props;
import akka.cluster.singleton.ClusterSingletonManager;
import akka.cluster.singleton.ClusterSingletonManagerSettings;
import akka.cluster.singleton.ClusterSingletonProxy;
import akka.cluster.singleton.ClusterSingletonProxySettings;
public class StatsSampleOneMasterMain {
public static void main(String[] args) {
if (args.length == 0) {
startup(new String[] { "2551", "2552", "0" });
StatsSampleOneMasterClientMain.main(new String[0]);
} else {
startup(args);
}
}
public static void startup(String[] ports) {
for (String port : ports) {
// Override the configuration of the port
Config config = ConfigFactory
.parseString("akka.remote.netty.tcp.port=" + port)
.withFallback(
ConfigFactory.parseString("akka.cluster.roles = [compute]"))
.withFallback(ConfigFactory.load("stats2"));
ActorSystem system = ActorSystem.create("ClusterSystem", config);
//#create-singleton-manager
ClusterSingletonManagerSettings settings = ClusterSingletonManagerSettings.create(system)
.withRole("compute");
system.actorOf(ClusterSingletonManager.props(
Props.create(StatsService.class), PoisonPill.getInstance(), settings),
"statsService");
//#create-singleton-manager
//#singleton-proxy
ClusterSingletonProxySettings proxySettings =
ClusterSingletonProxySettings.create(system).withRole("compute");
system.actorOf(ClusterSingletonProxy.props("/user/statsService",
proxySettings), "statsServiceProxy");
//#singleton-proxy
}
}
}

View file

@ -0,0 +1,85 @@
package docs.cluster;
import akka.cluster.routing.ClusterRouterGroup;
import akka.cluster.routing.ClusterRouterGroupSettings;
import akka.cluster.routing.ClusterRouterPool;
import akka.cluster.routing.ClusterRouterPoolSettings;
import akka.routing.ConsistentHashingGroup;
import akka.routing.ConsistentHashingPool;
import docs.cluster.StatsMessages.StatsJob;
import akka.actor.ActorRef;
import akka.actor.Props;
import akka.actor.UntypedActor;
import akka.routing.ConsistentHashingRouter.ConsistentHashableEnvelope;
import akka.routing.FromConfig;
import java.util.Collections;
//#service
public class StatsService extends UntypedActor {
// This router is used both with lookup and deploy of routees. If you
// have a router with only lookup of routees you can use Props.empty()
// instead of Props.create(StatsWorker.class).
ActorRef workerRouter = getContext().actorOf(
FromConfig.getInstance().props(Props.create(StatsWorker.class)),
"workerRouter");
@Override
public void onReceive(Object message) {
if (message instanceof StatsJob) {
StatsJob job = (StatsJob) message;
if (job.getText().equals("")) {
unhandled(message);
} else {
final String[] words = job.getText().split(" ");
final ActorRef replyTo = getSender();
// create actor that collects replies from workers
ActorRef aggregator = getContext().actorOf(
Props.create(StatsAggregator.class, words.length, replyTo));
// send each word to a worker
for (String word : words) {
workerRouter.tell(new ConsistentHashableEnvelope(word, word),
aggregator);
}
}
} else {
unhandled(message);
}
}
}
//#service
//not used, only for documentation
abstract class StatsService2 extends UntypedActor {
//#router-lookup-in-code
int totalInstances = 100;
Iterable<String> routeesPaths = Collections
.singletonList("/user/statsWorker");
boolean allowLocalRoutees = true;
String useRole = "compute";
ActorRef workerRouter = getContext().actorOf(
new ClusterRouterGroup(new ConsistentHashingGroup(routeesPaths),
new ClusterRouterGroupSettings(totalInstances, routeesPaths,
allowLocalRoutees, useRole)).props(), "workerRouter2");
//#router-lookup-in-code
}
//not used, only for documentation
abstract class StatsService3 extends UntypedActor {
//#router-deploy-in-code
int totalInstances = 100;
int maxInstancesPerNode = 3;
boolean allowLocalRoutees = false;
String useRole = "compute";
ActorRef workerRouter = getContext().actorOf(
new ClusterRouterPool(new ConsistentHashingPool(0),
new ClusterRouterPoolSettings(totalInstances, maxInstancesPerNode,
allowLocalRoutees, useRole)).props(Props
.create(StatsWorker.class)), "workerRouter3");
//#router-deploy-in-code
}

View file

@ -0,0 +1,30 @@
package docs.cluster;
import java.util.HashMap;
import java.util.Map;
import akka.actor.UntypedActor;
//#worker
public class StatsWorker extends UntypedActor {
Map<String, Integer> cache = new HashMap<String, Integer>();
@Override
public void onReceive(Object message) {
if (message instanceof String) {
String word = (String) message;
Integer length = cache.get(word);
if (length == null) {
length = word.length();
cache.put(word, length);
}
getSender().tell(length, getSelf());
} else {
unhandled(message);
}
}
}
//#worker

View file

@ -0,0 +1,60 @@
package docs.cluster;
import static docs.cluster.TransformationMessages.BACKEND_REGISTRATION;
import docs.cluster.TransformationMessages.TransformationJob;
import docs.cluster.TransformationMessages.TransformationResult;
import akka.actor.UntypedActor;
import akka.cluster.Cluster;
import akka.cluster.ClusterEvent.CurrentClusterState;
import akka.cluster.ClusterEvent.MemberUp;
import akka.cluster.Member;
import akka.cluster.MemberStatus;
//#backend
public class TransformationBackend extends UntypedActor {
Cluster cluster = Cluster.get(getContext().system());
//subscribe to cluster changes, MemberUp
@Override
public void preStart() {
cluster.subscribe(getSelf(), MemberUp.class);
}
//re-subscribe when restart
@Override
public void postStop() {
cluster.unsubscribe(getSelf());
}
@Override
public void onReceive(Object message) {
if (message instanceof TransformationJob) {
TransformationJob job = (TransformationJob) message;
getSender().tell(new TransformationResult(job.getText().toUpperCase()),
getSelf());
} else if (message instanceof CurrentClusterState) {
CurrentClusterState state = (CurrentClusterState) message;
for (Member member : state.getMembers()) {
if (member.status().equals(MemberStatus.up())) {
register(member);
}
}
} else if (message instanceof MemberUp) {
MemberUp mUp = (MemberUp) message;
register(mUp.member());
} else {
unhandled(message);
}
}
void register(Member member) {
if (member.hasRole("frontend"))
getContext().actorSelection(member.address() + "/user/frontend").tell(
BACKEND_REGISTRATION, getSelf());
}
}
//#backend

View file

@ -0,0 +1,48 @@
package docs.cluster;
import static docs.cluster.TransformationMessages.BACKEND_REGISTRATION;
import java.util.ArrayList;
import java.util.List;
import docs.cluster.TransformationMessages.JobFailed;
import docs.cluster.TransformationMessages.TransformationJob;
import akka.actor.ActorRef;
import akka.actor.Terminated;
import akka.actor.UntypedActor;
//#frontend
public class TransformationFrontend extends UntypedActor {
List<ActorRef> backends = new ArrayList<ActorRef>();
int jobCounter = 0;
@Override
public void onReceive(Object message) {
if ((message instanceof TransformationJob) && backends.isEmpty()) {
TransformationJob job = (TransformationJob) message;
getSender().tell(
new JobFailed("Service unavailable, try again later", job),
getSender());
} else if (message instanceof TransformationJob) {
TransformationJob job = (TransformationJob) message;
jobCounter++;
backends.get(jobCounter % backends.size())
.forward(job, getContext());
} else if (message.equals(BACKEND_REGISTRATION)) {
getContext().watch(getSender());
backends.add(getSender());
} else if (message instanceof Terminated) {
Terminated terminated = (Terminated) message;
backends.remove(terminated.getActor());
} else {
unhandled(message);
}
}
}
//#frontend

View file

@ -0,0 +1,63 @@
package docs.cluster;
import java.io.Serializable;
//#messages
public interface TransformationMessages {
public static class TransformationJob implements Serializable {
private final String text;
public TransformationJob(String text) {
this.text = text;
}
public String getText() {
return text;
}
}
public static class TransformationResult implements Serializable {
private final String text;
public TransformationResult(String text) {
this.text = text;
}
public String getText() {
return text;
}
@Override
public String toString() {
return "TransformationResult(" + text + ")";
}
}
public static class JobFailed implements Serializable {
private final String reason;
private final TransformationJob job;
public JobFailed(String reason, TransformationJob job) {
this.reason = reason;
this.job = job;
}
public String getReason() {
return reason;
}
public TransformationJob getJob() {
return job;
}
@Override
public String toString() {
return "JobFailed(" + reason + ")";
}
}
public static final String BACKEND_REGISTRATION = "BackendRegistration";
}
//#messages

View file

@ -0,0 +1,275 @@
package docs.ddata;
import static java.util.concurrent.TimeUnit.SECONDS;
import java.io.Serializable;
import java.util.HashSet;
import java.util.Optional;
import java.util.Set;
import scala.PartialFunction;
import scala.concurrent.duration.Duration;
import scala.runtime.BoxedUnit;
import akka.actor.AbstractActor;
import akka.actor.ActorRef;
import akka.actor.Props;
import akka.cluster.Cluster;
import akka.cluster.ddata.DistributedData;
import akka.cluster.ddata.Key;
import akka.cluster.ddata.LWWMap;
import akka.cluster.ddata.LWWMapKey;
import akka.cluster.ddata.Replicator;
import akka.cluster.ddata.Replicator.GetFailure;
import akka.cluster.ddata.Replicator.GetResponse;
import akka.cluster.ddata.Replicator.GetSuccess;
import akka.cluster.ddata.Replicator.NotFound;
import akka.cluster.ddata.Replicator.ReadConsistency;
import akka.cluster.ddata.Replicator.ReadMajority;
import akka.cluster.ddata.Replicator.Update;
import akka.cluster.ddata.Replicator.UpdateFailure;
import akka.cluster.ddata.Replicator.UpdateSuccess;
import akka.cluster.ddata.Replicator.UpdateTimeout;
import akka.cluster.ddata.Replicator.WriteConsistency;
import akka.cluster.ddata.Replicator.WriteMajority;
import akka.japi.pf.ReceiveBuilder;
@SuppressWarnings("unchecked")
public class ShoppingCart extends AbstractActor {
//#read-write-majority
private final WriteConsistency writeMajority =
new WriteMajority(Duration.create(3, SECONDS));
private final static ReadConsistency readMajority =
new ReadMajority(Duration.create(3, SECONDS));
//#read-write-majority
public static final String GET_CART = "getCart";
public static class AddItem {
public final LineItem item;
public AddItem(LineItem item) {
this.item = item;
}
}
public static class RemoveItem {
public final String productId;
public RemoveItem(String productId) {
this.productId = productId;
}
}
public static class Cart {
public final Set<LineItem> items;
public Cart(Set<LineItem> items) {
this.items = items;
}
}
public static class LineItem implements Serializable {
private static final long serialVersionUID = 1L;
public final String productId;
public final String title;
public final int quantity;
public LineItem(String productId, String title, int quantity) {
this.productId = productId;
this.title = title;
this.quantity = quantity;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((productId == null) ? 0 : productId.hashCode());
result = prime * result + quantity;
result = prime * result + ((title == null) ? 0 : title.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
LineItem other = (LineItem) obj;
if (productId == null) {
if (other.productId != null)
return false;
} else if (!productId.equals(other.productId))
return false;
if (quantity != other.quantity)
return false;
if (title == null) {
if (other.title != null)
return false;
} else if (!title.equals(other.title))
return false;
return true;
}
@Override
public String toString() {
return "LineItem [productId=" + productId + ", title=" + title + ", quantity=" + quantity + "]";
}
}
public static Props props(String userId) {
return Props.create(ShoppingCart.class, userId);
}
private final ActorRef replicator = DistributedData.get(context().system()).replicator();
private final Cluster node = Cluster.get(context().system());
@SuppressWarnings("unused")
private final String userId;
private final Key<LWWMap<String, LineItem>> dataKey;
public ShoppingCart(String userId) {
this.userId = userId;
this.dataKey = LWWMapKey.create("cart-" + userId);
}
@Override
public Receive createReceive() {
return matchGetCart()
.orElse(matchAddItem())
.orElse(matchRemoveItem())
.orElse(matchOther());
}
//#get-cart
private Receive matchGetCart() {
return receiveBuilder()
.matchEquals(GET_CART, s -> receiveGetCart())
.match(GetSuccess.class, this::isResponseToGetCart,
g -> receiveGetSuccess((GetSuccess<LWWMap<String, LineItem>>) g))
.match(NotFound.class, this::isResponseToGetCart,
n -> receiveNotFound((NotFound<LWWMap<String, LineItem>>) n))
.match(GetFailure.class, this::isResponseToGetCart,
f -> receiveGetFailure((GetFailure<LWWMap<String, LineItem>>) f))
.build();
}
private void receiveGetCart() {
Optional<Object> ctx = Optional.of(sender());
replicator.tell(new Replicator.Get<LWWMap<String, LineItem>>(dataKey, readMajority, ctx),
self());
}
private boolean isResponseToGetCart(GetResponse<?> response) {
return response.key().equals(dataKey) &&
(response.getRequest().orElse(null) instanceof ActorRef);
}
private void receiveGetSuccess(GetSuccess<LWWMap<String, LineItem>> g) {
Set<LineItem> items = new HashSet<>(g.dataValue().getEntries().values());
ActorRef replyTo = (ActorRef) g.getRequest().get();
replyTo.tell(new Cart(items), self());
}
private void receiveNotFound(NotFound<LWWMap<String, LineItem>> n) {
ActorRef replyTo = (ActorRef) n.getRequest().get();
replyTo.tell(new Cart(new HashSet<>()), self());
}
private void receiveGetFailure(GetFailure<LWWMap<String, LineItem>> f) {
// ReadMajority failure, try again with local read
Optional<Object> ctx = Optional.of(sender());
replicator.tell(new Replicator.Get<LWWMap<String, LineItem>>(dataKey, Replicator.readLocal(),
ctx), self());
}
//#get-cart
//#add-item
private Receive matchAddItem() {
return receiveBuilder()
.match(AddItem.class, this::receiveAddItem)
.build();
}
private void receiveAddItem(AddItem add) {
Update<LWWMap<String, LineItem>> update = new Update<>(dataKey, LWWMap.create(), writeMajority,
cart -> updateCart(cart, add.item));
replicator.tell(update, self());
}
//#add-item
private LWWMap<String, LineItem> updateCart(LWWMap<String, LineItem> data, LineItem item) {
if (data.contains(item.productId)) {
LineItem existingItem = data.get(item.productId).get();
int newQuantity = existingItem.quantity + item.quantity;
LineItem newItem = new LineItem(item.productId, item.title, newQuantity);
return data.put(node, item.productId, newItem);
} else {
return data.put(node, item.productId, item);
}
}
private Receive matchRemoveItem() {
return receiveBuilder()
.match(RemoveItem.class, this::receiveRemoveItem)
.match(GetSuccess.class, this::isResponseToRemoveItem,
g -> receiveRemoveItemGetSuccess((GetSuccess<LWWMap<String, LineItem>>) g))
.match(GetFailure.class, this::isResponseToRemoveItem,
f -> receiveRemoveItemGetFailure((GetFailure<LWWMap<String, LineItem>>) f))
.match(NotFound.class, this::isResponseToRemoveItem, n -> {/* nothing to remove */})
.build();
}
//#remove-item
private void receiveRemoveItem(RemoveItem rm) {
// Try to fetch latest from a majority of nodes first, since ORMap
// remove must have seen the item to be able to remove it.
Optional<Object> ctx = Optional.of(rm);
replicator.tell(new Replicator.Get<LWWMap<String, LineItem>>(dataKey, readMajority, ctx),
self());
}
private void receiveRemoveItemGetSuccess(GetSuccess<LWWMap<String, LineItem>> g) {
RemoveItem rm = (RemoveItem) g.getRequest().get();
removeItem(rm.productId);
}
private void receiveRemoveItemGetFailure(GetFailure<LWWMap<String, LineItem>> f) {
// ReadMajority failed, fall back to best effort local value
RemoveItem rm = (RemoveItem) f.getRequest().get();
removeItem(rm.productId);
}
private void removeItem(String productId) {
Update<LWWMap<String, LineItem>> update = new Update<>(dataKey, LWWMap.create(), writeMajority,
cart -> cart.remove(node, productId));
replicator.tell(update, self());
}
private boolean isResponseToRemoveItem(GetResponse<?> response) {
return response.key().equals(dataKey) &&
(response.getRequest().orElse(null) instanceof RemoveItem);
}
//#remove-item
private Receive matchOther() {
return receiveBuilder()
.match(UpdateSuccess.class, u -> {
// ok
})
.match(UpdateTimeout.class, t -> {
// will eventually be replicated
})
.match(UpdateFailure.class, f -> {
throw new IllegalStateException("Unexpected failure: " + f);
})
.build();
}
}

View file

@ -0,0 +1,130 @@
/**
* Copyright (C) 2009-2017 Lightbend Inc. <http://www.lightbend.com>
*/
package docs.persistence;
//#persistent-actor-example
import akka.actor.ActorRef;
import akka.actor.ActorSystem;
import akka.actor.Props;
import akka.persistence.AbstractPersistentActor;
import akka.persistence.SnapshotOffer;
import java.io.Serializable;
import java.util.ArrayList;
import static java.util.Arrays.asList;
class Cmd implements Serializable {
private static final long serialVersionUID = 1L;
private final String data;
public Cmd(String data) {
this.data = data;
}
public String getData() {
return data;
}
}
class Evt implements Serializable {
private static final long serialVersionUID = 1L;
private final String data;
public Evt(String data) {
this.data = data;
}
public String getData() {
return data;
}
}
class ExampleState implements Serializable {
private static final long serialVersionUID = 1L;
private final ArrayList<String> events;
public ExampleState() {
this(new ArrayList<>());
}
public ExampleState(ArrayList<String> events) {
this.events = events;
}
public ExampleState copy() {
return new ExampleState(new ArrayList<>(events));
}
public void update(Evt evt) {
events.add(evt.getData());
}
public int size() {
return events.size();
}
@Override
public String toString() {
return events.toString();
}
}
class ExamplePersistentActor extends AbstractPersistentActor {
private ExampleState state = new ExampleState();
public int getNumEvents() {
return state.size();
}
@Override
public String persistenceId() { return "sample-id-1"; }
@Override
public Receive createReceiveRecover() {
return receiveBuilder()
.match(Evt.class, state::update)
.match(SnapshotOffer.class, ss -> state = (ExampleState) ss.snapshot())
.build();
}
@Override
public Receive createReceive() {
return receiveBuilder()
.match(Cmd.class, c -> {
final String data = c.getData();
final Evt evt1 = new Evt(data + "-" + getNumEvents());
final Evt evt2 = new Evt(data + "-" + (getNumEvents() + 1));
persistAll(asList(evt1, evt2), (Evt evt) -> {
state.update(evt);
if (evt.equals(evt2)) {
getContext().system().eventStream().publish(evt);
}
});
})
.matchEquals("snap", s -> saveSnapshot(state.copy()))
.matchEquals("print", s -> System.out.println(state))
.build();
}
}
//#persistent-actor-example
public class PersistentActorExample {
public static void main(String... args) throws Exception {
final ActorSystem system = ActorSystem.create("example");
final ActorRef persistentActor = system.actorOf(Props.create(ExamplePersistentActor.class), "persistentActor-4-java8");
persistentActor.tell(new Cmd("foo"), null);
persistentActor.tell(new Cmd("baz"), null);
persistentActor.tell(new Cmd("bar"), null);
persistentActor.tell("snap", null);
persistentActor.tell(new Cmd("buzz"), null);
persistentActor.tell("print", null);
Thread.sleep(10000);
system.terminate();
}
}