akka cluster in java - jcconf 2015
TRANSCRIPT
Introduction
Akka
Concurrent & Distributed
Actor Model
http://blog.shiftehfar.org/?p=431
Scala
Java?
Scala is better
Java
Maven
Basic
pom.xml
<dependencies><dependency>
<groupId>com.typesafe.akka</groupId><artifactId>akka-actor_2.11</artifactId><version>2.4.1</version>
</dependency></dependencies>
<repositories><repository>
<id>typesafe</id><name>Typesafe Repository</name><url>http://repo.typesafe.com/typesafe/releases/</url>
</repository></repositories>
Main.java
public static void main(String[] args) {// akka.Main.main(new String[]{HelloWorld.class.getName()});
ActorSystem system = ActorSystem.create("Hello");ActorRef a =
system.actorOf(Props.create(HelloWorld.class), "helloWorld");}
HelloWorld Actor
public class HelloWorld extends UntypedActor {
@Overridepublic void preStart() {
final ActorRef greeter = getContext().actorOf(Props.create(Greeter.class), "greeter");
greeter.tell(Greeter.Msg.GREET, getSelf());}
@Overridepublic void onReceive(Object msg) {
if (msg == Greeter.Msg.DONE) {getContext().stop(getSelf());
} elseunhandled(msg);
}}
Greeter Actor
public class Greeter extends UntypedActor {
public static enum Msg {GREET, DONE;
}
@Overridepublic void onReceive(Object msg) {
if (msg == Msg.GREET) {System.out.println("Hello World!");getSender().tell(Msg.DONE, getSelf());
} elseunhandled(msg);
}
}
Sending message
• tell() – Fire and forget• ask() – Send and receive
Avoid Ask
• https://www.safaribooksonline.com/library/view/effective-akka/9781449360061/ch02.html#_avoiding_ask
• https://www.safaribooksonline.com/library/view/effective-akka/9781449360061/ch03.html#_tell_don_8217_t_ask
Remoting
pom.xml
<dependency><groupId>com.typesafe.akka</groupId><artifactId>akka-remote_2.11</artifactId><version>2.4.1</version>
</dependency>
Local application.conf
LocalSys {akka {
actor {provider = "akka.remote.RemoteActorRefProvider"
}remote {enabled-transports = ["akka.remote.netty.tcp"]netty.tcp {hostname = "127.0.0.1"port = 2551
}}
}}
Remote application.conf
RemoteSys {akka {
actor {provider = "akka.remote.RemoteActorRefProvider"
}remote {enabled-transports = ["akka.remote.netty.tcp"]netty.tcp {hostname = "127.0.0.1"port = 2552
}}
}}
Local Main
public static void main(String[] args) throws Exception {
ActorSystem _system = ActorSystem.create("LocalNodeApp",ConfigFactory
.load().getConfig("LocalSys"));ActorRef localActor =
_system.actorOf(Props.create(LocalActor.class));localActor.tell("Hello", null);
Thread.sleep(5000);_system.shutdown();
}
Local Actor
ActorRef remoteActor;
@Overridepublic void preStart() {
//Get a reference to the remote actorremoteActor = getContext().actorFor(
"akka.tcp://[email protected]:2552/user/remoteActor");}
@Overridepublic void onReceive(Object message) throws Exception {
Future<Object> future = Patterns.ask(remoteActor, message.toString(),
timeout);String result = (String) Await.result(future,
timeout.duration());log.info("Message received from Server -> {}", result);
}
Actor Path
akka.<protocol>://<actorsystemname>@<hostname>:<port>/<actor path>
• http://doc.akka.io/docs/akka/2.4.1/general/addressing.html
Remote Main
public static void main(String[] args) {
final ActorSystem system = ActorSystem.create("RemoteNodeApp", ConfigFactory
.load().getConfig("RemoteSys"));
system.actorOf(Props.create(RemoteActor.class), "remoteActor");
Runtime.getRuntime().addShutdownHook(new Thread() {
@Overridepublic void run() {
system.shutdown();}
});}
Remote Actor
@Overridepublic void onReceive(Object message) throws Exception {
if (message instanceof String) {// Get reference to the message sender and
reply backlog.info("Message received -> {}", message);getSender().tell(message + " got something",
null);}
}
Router
Router Mainpublic static void main(String[] args) throws InterruptedException {
ActorSystem _system = ActorSystem.create("RemoteRouteeRouterExample", ConfigFactory.load().getConfig("MyRouterExample"));
Address[] addresses = new Address[]{new Address("akka.tcp", "RemoteNodeApp",
"10.211.55.6", 2552),new Address("akka.tcp", "RemoteNodeApp",
"10.211.55.5", 2552)};
ActorRef router = _system.actorOf(new RemoteRouterConfig(new RoundRobinPool(5), addresses).props(
Props.create(RemoteActor.class)));
for (int i = 1; i <= 10; i++) {// sends randomly to actorsrouter.tell("Hello " + Integer.toString(i), null);
}_system.shutdown();
}
Cluster
pom.xml
<dependency><groupId>com.typesafe.akka</groupId><artifactId>akka-cluster_2.11</artifactId><version>2.4.1</version>
</dependency>
application.conf
akka {actor {provider = "akka.cluster.ClusterActorRefProvider"
}remote {log-remote-lifecycle-events = offnetty.tcp {hostname = "127.0.0.1"port = 0
}}
cluster {seed-nodes = ["akka.tcp://[email protected]:2552","akka.tcp://[email protected]:2552"]
auto-down-unreachable-after = 10s}
}
Seed Nodes
Member States
Backend Mainpublic static void main(String[] args) {
// Override the configuration of the port when specified as program argument
final String hostname = args.length > 0 ? args[0] : "127.0.0.1";final String port = args.length > 1 ? args[1] : "0";
final Config config = ConfigFactory.parseString("akka.remote.netty.tcp.hostname=" + hostname).
withFallback(ConfigFactory.parseString("akka.remote.netty.tcp.port=" + port)).
withFallback(ConfigFactory.parseString("akka.cluster.roles = [backend]")).
withFallback(ConfigFactory.load());
ActorSystem system = ActorSystem.create("ClusterSystem", config);
system.actorOf(Props.create(TransformationBackend.class), "backend");
}
Backend Actor - 1
Cluster cluster = Cluster.get(getContext().system());
//subscribe to cluster changes, MemberUp@Overridepublic void preStart() {cluster.subscribe(getSelf(), MemberUp.class);
}
//re-subscribe when restart@Overridepublic void postStop() {cluster.unsubscribe(getSelf());
}
void register(Member member) {if (member.hasRole("frontend"))getContext().actorSelection(member.address() +
"/user/frontend").tell(BACKEND_REGISTRATION, getSelf());
}
Backend Actor - 2@Overridepublic 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);
}}
Frontend Main - 1
final String hostname = args.length > 0 ? args[0] : "127.0.0.1";final String port = args.length > 1 ? args[1] : "0";
final Config config = ConfigFactory.parseString("akka.remote.netty.tcp.hostname=" + hostname).
withFallback(ConfigFactory.parseString("akka.remote.netty.tcp.port=" + port)).
withFallback(ConfigFactory.parseString("akka.cluster.roles = [frontend]")).
withFallback(ConfigFactory.load());
ActorSystem system = ActorSystem.create("ClusterSystem", config);
Frontend Main - 2
final ActorRef frontend = system.actorOf(Props.create(TransformationFrontend.class),
"frontend");…system.scheduler().schedule(interval, interval, new Runnable() {
public void run() {Patterns.ask(frontend,
new TransformationJob("hello-" + counter.incrementAndGet()),
timeout).onSuccess(new OnSuccess<Object>() {
public void onSuccess(Object result) {System.out.println(result);
}}, ec);
}
}, ec);
Frontend Actor - 1
List<ActorRef> backends = new ArrayList<ActorRef>();int jobCounter = 0;
@Overridepublic 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());
Frontend Actor - 2
} 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);
}}
Run
java -cp .:./* sample.cluster.transformation.TransformationBackendMain 10.211.55.5 2552
java -cp .:./* sample.cluster.transformation.TransformationBackendMain 10.211.55.6 2552
java -cp .:./* sample.cluster.transformation.TransformationFrontendMain 10.211.55.2 2552
Best Practices
• At least two seed nodes• Use fixed port if possible
Something not mentioned
• Supervision• Persistence• …
ZooKeeper
3 ZooKeeper Servers
pom.xml
<dependency><groupId>com.sclasen</groupId><artifactId>akka-zk-cluster-seed_2.11</artifactId><version>0.1.2</version>
</dependency>
application.conf
akka {loglevel = "DEBUG"stdout-loglevel = "DEBUG"actor {provider = "akka.cluster.ClusterActorRefProvider"
}remote {log-remote-lifecycle-events = offnetty.tcp {hostname = "127.0.0.1"port = 0
}}
cluster {// seed-nodes = [// "akka.tcp://[email protected]:2552",// "akka.tcp://[email protected]:2552"]
auto-down-unreachable-after = 10s}
}
reference.conf
akka.cluster.seed.zookeeper {url = "10.211.55.2:2181,10.211.55.5:2181,10.211.55.6:2181"path = "/akka/cluster/seed"
}
Join Cluster
ActorSystem system = ActorSystem.create("ClusterSystem", config);new ZookeeperClusterSeed((ExtendedActorSystem)system).join();
Cluster & Router
factorial.conf - 1
include "application"
# //#min-nr-of-membersakka.cluster.min-nr-of-members = 3# //#min-nr-of-members
# //#role-min-nr-of-membersakka.cluster.role {frontend.min-nr-of-members = 1backend.min-nr-of-members = 2
}# //#role-min-nr-of-members
factorial.conf - 2
# //#adaptive-routerakka.actor.deployment {/factorialFrontend/factorialBackendRouter = {
router = adaptive-group# metrics-selector = heap# metrics-selector = load# metrics-selector = cpumetrics-selector = mixnr-of-instances = 100routees.paths = ["/user/factorialBackend"]cluster {enabled = onuse-role = backendallow-local-routees = off
}}
}# //#adaptive-router
Backend Main
final String port = args.length > 0 ? args[0] : "0";final Config config = ConfigFactory.parseString("akka.remote.netty.tcp.port=" + port).
withFallback(ConfigFactory.parseString("akka.cluster.roles= [backend]")).
withFallback(ConfigFactory.load("factorial"));
ActorSystem system = ActorSystem.create("ClusterSystem", config);new ZookeeperClusterSeed((ExtendedActorSystem)system).join();
system.actorOf(Props.create(FactorialBackend.class), "factorialBackend");
system.actorOf(Props.create(MetricsListener.class), "metricsListener");
Backend Actor
Frontend Main
final int upToN = 200;
final Config config = ConfigFactory.parseString("akka.cluster.roles = [frontend]").withFallback(ConfigFactory.load("factorial"));
final ActorSystem system = ActorSystem.create("ClusterSystem", config);new ZookeeperClusterSeed((ExtendedActorSystem)system).join();
Cluster.get(system).registerOnMemberUp(new Runnable() {@Overridepublic void run() {
system.actorOf(Props.create(FactorialFrontend.class, upToN, true),
"factorialFrontend");}
});
Frontend Actor
ActorRef backend = getContext().actorOf(FromConfig.getInstance().props(),
"factorialBackendRouter");
…
void sendJobs() {log.info("Starting batch of factorials up to
[{}]", upToN);for (int n = 1; n <= upToN; n++) {
backend.tell(n, getSelf());}
}
Resources
Java Sample Code
https://github.com/jiayun/akka_samples
https://www.safaribooksonline.com/search/?query=Akka&
highlight=true
Thanks