Akka Cluster in Java - JCConf 2015

Post on 09-Jan-2017

2.581 views 1 download

Transcript of Akka Cluster in Java - JCConf 2015

#JCConf

Akka Cluster in Java

Jiayun Zhoujiayun@gmail.com

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://RemoteNodeApp@127.0.0.1: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://ClusterSystem@10.211.55.5:2552","akka.tcp://ClusterSystem@10.211.55.6: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://ClusterSystem@10.211.55.5:2552",// "akka.tcp://ClusterSystem@10.211.55.6: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