87
INTRODUCTION TO AKKA Roy Russo 1

Introduction to Akka - Atlanta Java Users Group

Embed Size (px)

Citation preview

INTRODUCTION TO AKKARoy Russo

1

Who am I?

• Former JBoss Portal Co-Founder

• LoopFuse Co-Founder

• ElasticHQ Founder

• http://www.elastichq.org

• Co-Author, Elasticsearch In Action

• Chief Architect, Altisource Labs

• VP Engineering, Predikto

2

Agenda

• The case for an Actor Model

• Akka

• Actor Systems

• Deployment Diagrams

3

Moore’s Law

The number of transistors on a chip will double

approximately every 18 months.

~ Gordon Moore, 1965

4

Moore’s Law

5

Moore’s Law

• Early 2000:

• Chips got BIG

• Signals couldn’t reach whole chip in one clock

cycle.

• Heat dissipation issues.

6

Moore’s Law

• Multi-core processors

• Multiple calculations in parallel

• CPU speeds are likely to not increase any time soon.

7

The Free Lunch is Over

• Build your applications to…

• take advantage of advances in new processors.

• take advantage of distributed computing.

• operate in a concurrent fashion!

8

Parallelize!

• Parallelism: When two threads are executing

simultaneously.

9

Threads

10

• Shared (mutable) state

• Deadlocks

• LiveLocks

• Thread Starvation

• Race Condition

“95% of synchronized

code is broken. The

other 5% is written by

Brian Goetz.”

11

Is Joe Armstrong Right?

• The world is concurrent. Things happen in parallel.

• Groups of people have no shared state.

• You have your memory. I have mine.

• Communication by messages; sound, light.

• State updates based on messages.

• Sh*t happens. (Things Fail)

• From: https://pragprog.com/articles/erlang

12

Alternatives?

13

Software Transactional Memory (STM)

Message-Passing Concurrency (Actors)

Dataflow Concurrency

14

ACTORS

Actors

• From a 1973 paper, written by Carl Hewitt

• Erlang, Occam, Oz

• Encapsulate state and behavior

• Implement Message-Passing Concurrency

15

• Share Nothing.

• No need to synchronize.

• Isolated. Lightweight event-based Processes.

• ~ 6.5m on 4GB RAM

• Communicate through Messages.

• Asychronous and Non-Blocking

• Messages are immutable

• Each actor has a mailbox (message queue)

• Supervision-based failure management.

16

Actor Model of Concurrency

17

18

Akka

Akka is a toolkit for building

Concurrent

Distributed

Fault-Tolerant

applications.

19

Actors

Remoting

Supervision

When to Akka?

• When you need to build a system that:

• reacts to Events … Event-Driven

• reacts to Load … Scalable

• reacts to Failure … Resilient

• reacts to Users … Responsive

20

REACTIVE APPLICATIONS

Actor System

Chat Architecture

21

Cassandra

Cluster

Elasticsearch

Cluster

User 1

Controllers

Services

websocket

Users

websocket

async

async async

async

Akka Actor

22

Actor

Behavior

State

mailbox• Lightweight object

• ~300 bytes/instance

• Keeps internal state

• Asynch / non-blocking

• Messages kept in mailbox

• Persistence

• Small call stack =

• Scalable and fast

When an actor receives a message…

23

• Actors can:

• Create new Actors

• Sends messages to

Actors

• Designate how it should

handle the next message

Actor

Behavior

State

mailbox

24

AKKA

Concurr

ent

(more

core

s)

Distributed (more nodes)

Scale UP & OUT

Some code, for the bored…

25

Sample pom

26

<dependency>

<groupId>com.typesafe.akka</groupId>

<artifactId>akka-actor_2.10</artifactId>

<version>${akka.version}</version>

</dependency>

<dependency>

<groupId>com.typesafe.akka</groupId>

<artifactId>akka-persistence-experimental_2.10</artifactId>

<version>${akka.version}</version>

</dependency>

<dependency>

<groupId>com.typesafe.akka</groupId>

<artifactId>akka-cluster_2.10</artifactId>

<version>${akka.version}</version>

</dependency>

<dependency>

<groupId>com.typesafe.akka</groupId>

<artifactId>akka-slf4j_2.10</artifactId>

<version>${akka.version}</version>

</dependency>

<dependency>

<groupId>org.scala-lang</groupId>

<artifactId>scala-library</artifactId>

<version>${scala.version}</version>

</dependency>

Actor System

27

• Actors created within this context/container.

• Manage shared facilities:

• scheduling, logging, configuration, etc…

• Can have many per JVM with different configs

• Can contain millions of actors.

Actor System

28

public void init() throws Exception {

actorSystem = ActorSystem.create("chatActorSystem");

}

Actor System

29

<bean id="actorSystemService" class="com.streamproject.akka.ActorSystemService"

init-method="init" destroy-method="destroy">

</bean>

On app-boot with Spring… (applicationContext.xml)

Actor System

30

/**

* Note that this will throw on servlet container shutdown. They are false alarms in Tomcat regarding

* Threads not being terminated correctly. The Thread.sleep call is merely here to make the noise less

* noisy.

*/

public void destroy() throws Exception {

actorSystem.shutdown();

Thread.sleep(5000);

}

Actor System - Configuration

31

akka {

# Loggers to register at boot time (akka.event.Logging$DefaultLogger logs

# to STDOUT)

loggers = ["akka.event.slf4j.Slf4jLogger"]

# Log level used by the configured loggers (see "loggers") as soon

# as they have been started; before that, see "stdout-loglevel"

# Options: OFF, ERROR, WARNING, INFO, DEBUG

loglevel = "DEBUG"

# Log level for the very basic logger activated during ActorSystem startup.

# This logger prints the log messages to stdout (System.out).

# Options: OFF, ERROR, WARNING, INFO, DEBUG

stdout-loglevel = "DEBUG"

actor {

provider = "akka.cluster.ClusterActorRefProvider"

}

application.conf || application.json || application.properties

Actor System - Configuration

32

public void init() throws Exception {

Config conf = ConfigFactory.load(“myconfig”);

actorSystem = ActorSystem.create(“chatActorSystem”, conf);

}

myconfig.conf

• Custom config naming/loading

• Configuration by string

• Override configuration values

Actor System

33

Actor System

Actor System location transparency

Event

• Remote and local process

actors treated the same

• Unified prog. model for

multicore and distributed

computing

msg

ActorRef

34

ActorRef MailboxActor

Instance

Points to Actor.

Delivers to Mailbox

Mailbox

35

ActorRef MailboxActor

Instance

Invokes Actor Instance with Message.

Runs on a dispatcher - abstracts threading

Actor Instance

36

ActorRef MailboxActor

Instance

Your Code Here

The 4 Actor Operations

37

The 4 Actor Operations

• DEFINE

1. CREATE

2. SEND

3. BECOME

4. SUPERVISE

38

DEFINE

39

public class MyActor extends UntypedActor {

public void onReceive(Object message) throws Exception {

}

}

CREATE

40

ActorSystem mySystem = ActorSystem.create("MyActorSystem");

ActorRef myActor = mySystem.actorOf(new Props(MyActor.class),"myActor");

Creates a top-level Actor

DEFINE - non-default constructor

41

public class PersistEventProcessor extends UntypedActor {

private SearchService searchService;

public PersistEventProcessor(SearchService searchService) {

this.messageEventService = messageEventService;

this.searchService = searchService;

}

@Override

public void onReceive(Object obj) throws Exception {…}

}

CREATE - non-default constructor

42

final ActorRef actorRef = actorSystemService.getActorSystem()

.actorOf(Props.create(PersistEventProcessor.class, searchService));

actorRef.tell(new PersistMessageEventCommand(event), null);

CREATE - Child Actor

43

public class SupervisorActor extends UntypedActor

{

ActorRef myChildActor = getContext().actorOf(new

Props(MyChildActor.class), “myChildActor” +

UUID.randomUUID());

}

SEND

44

• Fire and Forget

• Asynch

• No expected reply

• tell()

• Send and Receive

• Asynch

• Expects reply (Future)

• ask()

SEND - tell()

45

actor.tell(message);

// Explicit passing of sender actor reference

actor.tell(msg, getSelf());

SEND - ask()

46

import static akka.patterns.Patterns.ask;

Future<Object> future = ask(actor, message, timeout);

future.onSuccess(new OnSuccess<Object>() {

public void onSuccess(String result) {

System.out.println(result);

}

});

more on Futures in a bit…

SEND - An ask() reply

47

try {

String result = operation();

getSender().tell(result, getSelf());

} catch (Exception e) {

getSender().tell(new akka.actor.Status.Failure(e), getSelf());

throw e;

}

BECOME

• Dynamically redefines actor behavior

• Reactively triggered by message

• Like changing an interface, or implementation on-the-fly

48

BECOME

49

public class PingPongActor extends UntypedActor {

static String PING = "PING";

static String PONG = "PONG";

int count = 0;

public void onReceive(Object message) throws Exception {

if (message instanceof String) {

if (((String) message).matches(PING)) {

System.out.println("PING");

count += 1;

Thread.sleep(100);

getSelf().tell(PONG);

getContext().become(new Procedure<Object>() {

public void apply(Object message) {

if (message instanceof String) {

// do Something

}

}

}

});

if (count > 10)

getContext().stop(getSelf());

}

SUPERVISE

• Manage another actor’s failure

• Supervisors receive notifications, and can react upon

failure

• Every actor has a default supervisor strategy

• Can be overriden

• OneForOneStrategy (def)

• AllForOneStrategy

50

SUPERVISE

51

• On Failure, a supervisor defines what happens next:

• resume child, and keep internal state

• restart child, and wipe internal state

• stop child permanently

• stop itself and escalate the error

• AllForOneStrategy, affects all children.

SUPERVISE

52

public class Supervisor extends UntypedActor {

private static SupervisorStrategy strategy =

new OneForOneStrategy(10, Duration.create("1 minute"),

new Function<Throwable, Directive>() {

@Override

public Directive apply(Throwable t) {

if (t instanceof ArithmeticException) {

return resume();

} else if (t instanceof NullPointerException) {

return restart();

} else if (t instanceof IllegalArgumentException) {

return stop();

} else {

return escalate();

}

}

});

@Override

public SupervisorStrategy supervisorStrategy() {

return strategy;

}

SU

PE

RV

ISE

53

http://doc.akka.io/docs/akka/snapshot/java/fault-tolerance-sample.html

SUPERVISE

• A contrast to Failure Management in Java

• You get one thread to control.

• If the thread blows up, then what?

• Defensive programming:

• Mix error handling within the thread

• == tangled business logic + error handling

• Errors don’t propagate between threads

54

Stopping Actors

55

//first option of shutting down the actors by shutting down the ActorSystem

system.shutdown();

//second option of shutting down the actor by sending a poisonPill message

actor.tell(PoisonPill.getInstance());

//third option of shutting down the actor

getContext().stop(getSelf());

//or

getContext().stop(childActorRef);

Killing Actors

56

actor.tell(Kill.getInstance());

• Synchronous

• Sends ActorKilledException to parent

UntypedActor API

• getSelf() : me

• getSender() : reply-to

• getContext() :

• factory for child actors

• get parent

• system the actor belongs to

• supervisionStrategy()

• onReceive(msg)

57

UntypedActor API

58

public void preStart() {

}

public void preRestart(Throwable reason, scala.Option<Object> message) {

for (ActorRef each : getContext().getChildren()) {

getContext().unwatch(each);

getContext().stop(each);

}

postStop();

}

public void postRestart(Throwable reason) {

preStart();

}

public void postStop() {

}

Acto

r Life

Cycle

59

Message Delivery

60

ActorRef MailboxActor

Instance

Invokes Actor Instance with Message.

Runs on a dispatcher - abstracts threading

• At-Most-Once (Akka)

• At-Least-Once (Akka Persistence)

• Exactly Once

• What’s the definition of “Guaranteed Delivery”?

An Example

61

public class UserLoginProcessor extends UntypedActor {

LoggingAdapter log = Logging.getLogger(getContext().system(), this);

CoreUserDetailsServiceImpl coreUserDetailsService;

public UserLoginProcessor(CoreUserDetailsServiceImpl userDetailsService) {

coreUserDetailsService = userDetailsService;

}

@Override

public void onReceive(Object message) throws Exception {

if (message instanceof ProcessLoginCommand) {

SomeEvent someEvent = ((ProcessLoginCommand) message).getSomeEvent();

User user = UserDTOUtil.fromDTOToUser(someEvent.getUserDTO());

coreUserDetailsService.updateLoginTS(user);

} else if (message instance ProcessLogoutCommand) { … }

}

}

Akka Logging

non-def constructor

Command-based messaging

An Example

62

public class ProcessLoginCommand extends BaseCommand {

private StreamEvent streamEvent;

public ProcessLoginCommand(Object streamEvent) {

super(streamEvent);

this.streamEvent = (StreamEvent) streamEvent;

}

public StreamEvent getStreamEvent() {

return streamEvent;

}

}

public class BaseCommand<T> {

private T commandObject;

public BaseCommand(T commandObject) {

this.commandObject = commandObject;

}

public T getCommandObject() {

return commandObject;

}

}

An Example

63

@Autowired

private CoreUserDetailsServiceImpl userService;

@Override

public void processInboundEvent(StreamEvent event) {

switch (event.getEventType()) {

case EventType.USER_LOGIN: {

final ActorRef actorRef = actorSystemService.getActorSystem()

.actorOf(Props.create(UserLoginProcessor.class, userService), "login-processor

actorRef.tell(new ProcessLoginCommand(event), null);

An Example

64

public class UserLoginProcessor extends UntypedActor {

LoggingAdapter log = Logging.getLogger(getContext().system(),

this);

CoreUserDetailsServiceImpl coreUserDetailsService;

public UserLoginProcessor(CoreUserDetailsServiceImpl

userDetailsService) {

coreUserDetailsService = userDetailsService;

}

}

Dispatchers

65

• Control execution flow

• based on the Java Executor framework

(java.util.concurrent)

• ThreadPoolExecutor: Executes each submitted task

using thread from a predefined and configured thread

pool.

• ForkJoinPool: Same thread pool model but

supplemented with work stealing. Threads in the pool

will find and execute tasks (work stealing) created by

other active tasks or tasks allocated to other threads in

the pool that are pending execution.

Dispatchers

66

Dispatchers

67

default-dispatcher {

type = "Dispatcher"

executor = "fork-join-executor"

fork-join-executor {

parallelism-min = 8

parallelism-factor = 3.0

parallelism-max = 64

}

thread-pool-executor {

keep-alive-time = 60s

core-pool-size-min = 8

core-pool-size-factor = 3.0

core-pool-size-max = 64

max-pool-size-min = 8

max-pool-size-factor = 3.0

max-pool-size-max = 64

task-queue-size = -1

task-queue-type = "linked"

allow-core-timeout = on

Dispatcher

68

Pinned Dispatcher

69

Balancing Dispatcher

70

Mailbox Types

71

• Backed by queue impl. from java.util.concurrent

bounded-mailbox {

mailbox-type = "akka.dispatch.BoundedMailbox"

mailbox-capacity = 1000

}

Priority Mailboxes

72

public class MyPrioMailbox extends UnboundedPriorityMailbox {

// needed for reflective instantiation

public MyPrioMailbox(ActorSystem.Settings settings, Config config) {

// Create a new PriorityGenerator, lower prio means more important

super(new PriorityGenerator() {

@Override

public int gen(Object message) {

if (message.equals("highpriority"))

return 0; // 'highpriority messages should be treated first if possible

else if (message.equals("lowpriority"))

return 2; // 'lowpriority messages should be treated last if possible

else if (message.equals(PoisonPill.getInstance()))

return 3; // PoisonPill when no other left

else

return 1; // By default they go between high and low prio

}

});

}

}

Routers

73

Routers

74

• Round-Robin Router

• Random Router

• Smallest Mailbox Router

• Broadcast Router

ActorRef router = system.actorOf(new Props(MyActor.class).

withRouter(new RoundRobinRouter(nrOfInstances)),"myRouterActor");

int lowerBound = 2;

int upperBound = 15;

DefaultResizer resizer = new DefaultResizer(lowerBound, upperBound);

ActorRef randomRouter = _system.actorOf(new Props(MsgEchoActor.class). withRouter(new RandomRouter(resizer)));

Futures

75

Timeout timeout = new Timeout(Duration.create(5, "seconds"));

Future<Object> future = Patterns.ask(actor, msg, timeout);

String result = (String) Await.result(future, timeout.duration());

Blocking!

• Callbacks: onSuccess, onFailure, onComplete

Future<Integer> future = future(new Callable<Integer>() {

public Integer call() {

return 1 / 0;

}

}, ec).recover(new Recover<Integer>() {

public Integer recover(Throwable problem) throws Throwable {

if (problem instanceof ArithmeticException)

return 0;

else

throw problem;

}

}, ec);

Persistence

• Experimental (from “Eventsourced”)

• Plugins: LevelDB, MongoDB, Cassandra, etc..

• Architecture:

• PersistentActor

• Journal

• AtLeastOnce Delivery

• Snapshots

• Event Sourcing (http://martinfowler.com/eaaDev/EventSourcing.html)

76

Some Code…

No Persistence

77

count = 0

No Persistence

78

count = 1

CRASH!

No Persistence

79

count = 0

RESTART!

No Persistence

80

count =1

Restarted

No Persistence

81

count =1

WRONG!

expected count == 2

With Persistence

82

Journal DB

count =0

Restarted

With Persistence

83

Journal DB

count = 2

Restarted

Example: Event-Sourcing

84

Event Bus

• Used internally by Akka: Event Stream, Dead Letters,

Logging, etc…

• Remember: No Sender access!

85

public boolean subscribe(Subscriber subscriber, Classifier to);

public boolean unsubscribe(Subscriber subscriber, Classifier from);

public void unsubscribe(Subscriber subscriber);

public void publish(Event event);

Some Code…

Stuff I didn’t cover

• Testing

• Typed Actors

• Application Monitoring

• MANY Configuration options

• http://doc.akka.io/docs/akka/snapshot/general/configurati

on.html

86

Questions?

87

• Beginner Examples: https://github.com/royrusso/akka-

java-examples

• Akka Java Doc:

http://doc.akka.io/docs/akka/snapshot/java.html