Building Reactive Applications with Akka & Java 8 - Bonèr

Post on 06-May-2015

640 views 1 download

description

Slides from Jonas Bonèr talk @ codemotion roma 2014

Transcript of Building Reactive Applications with Akka & Java 8 - Bonèr

Building Reactive Applications with Akka & Java 8

Jonas Bonér Typesafe

CTO & co-founder Twitter: @jboner

The rules of the game

have changed

3

Apps in the 60s-90s were written for

Apps today are written for

3

Apps in the 60s-90s were written for

Apps today are written for

Single machines

3

Apps in the 60s-90s were written for

Apps today are written for

Single machines Clusters of machines

3

Apps in the 60s-90s were written for

Apps today are written for

Single machines Clusters of machines

Single core processors

3

Apps in the 60s-90s were written for

Apps today are written for

Single machines Clusters of machines

Single core processors Multicore processors

3

Apps in the 60s-90s were written for

Apps today are written for

Single machines Clusters of machines

Single core processors Multicore processors

Expensive RAM

3

Apps in the 60s-90s were written for

Apps today are written for

Single machines Clusters of machines

Single core processors Multicore processors

Expensive RAM Cheap RAM

3

Apps in the 60s-90s were written for

Apps today are written for

Single machines Clusters of machines

Single core processors Multicore processors

Expensive RAM Cheap RAM

Expensive disk

3

Apps in the 60s-90s were written for

Apps today are written for

Single machines Clusters of machines

Single core processors Multicore processors

Expensive RAM Cheap RAM

Expensive disk Cheap disk

3

Apps in the 60s-90s were written for

Apps today are written for

Single machines Clusters of machines

Single core processors Multicore processors

Expensive RAM Cheap RAM

Expensive disk Cheap disk

Slow networks

3

Apps in the 60s-90s were written for

Apps today are written for

Single machines Clusters of machines

Single core processors Multicore processors

Expensive RAM Cheap RAM

Expensive disk Cheap disk

Slow networks Fast networks

3

Apps in the 60s-90s were written for

Apps today are written for

Single machines Clusters of machines

Single core processors Multicore processors

Expensive RAM Cheap RAM

Expensive disk Cheap disk

Slow networks Fast networks

Few concurrent users

3

Apps in the 60s-90s were written for

Apps today are written for

Single machines Clusters of machines

Single core processors Multicore processors

Expensive RAM Cheap RAM

Expensive disk Cheap disk

Slow networks Fast networks

Few concurrent users Lots of concurrent users

3

Apps in the 60s-90s were written for

Apps today are written for

Single machines Clusters of machines

Single core processors Multicore processors

Expensive RAM Cheap RAM

Expensive disk Cheap disk

Slow networks Fast networks

Few concurrent users Lots of concurrent users

Small data sets

3

Apps in the 60s-90s were written for

Apps today are written for

Single machines Clusters of machines

Single core processors Multicore processors

Expensive RAM Cheap RAM

Expensive disk Cheap disk

Slow networks Fast networks

Few concurrent users Lots of concurrent users

Small data sets Large data sets

3

Apps in the 60s-90s were written for

Apps today are written for

Single machines Clusters of machines

Single core processors Multicore processors

Expensive RAM Cheap RAM

Expensive disk Cheap disk

Slow networks Fast networks

Few concurrent users Lots of concurrent users

Small data sets Large data sets

Latency in seconds

3

Apps in the 60s-90s were written for

Apps today are written for

Single machines Clusters of machines

Single core processors Multicore processors

Expensive RAM Cheap RAM

Expensive disk Cheap disk

Slow networks Fast networks

Few concurrent users Lots of concurrent users

Small data sets Large data sets

Latency in seconds Latency in milliseconds

Cost Gravity is at Work

5

Cost Gravity is at Work

5

Reactive  Applications

Reactive applications share four traits

6

Reactive applications react to changes in the world around them.

Event-Driven• Loosely coupled architecture, easier to extend, maintain, evolve

• Asynchronous and non-blocking • Concurrent by design, immutable state • Lower latency and higher throughput

8

“Clearly, the goal is to do these operations concurrently and non-blocking, so that entire blocks of seats or sections are not locked.

We’re able to find and allocate seats under load in less than 20ms without trying very hard to achieve it.”  

Andrew Headrick, Platform Architect, Ticketfly

Introducing the Actor Model

10

The Actor Model

10

A computational model that embodies:

The Actor Model

10

A computational model that embodies:

✓ Processing

The Actor Model

10

A computational model that embodies:

✓ Processing

✓ Storage

The Actor Model

10

A computational model that embodies:

✓ Processing

✓ Storage

✓ Communication

The Actor Model

10

A computational model that embodies:

✓ Processing

✓ Storage

✓ Communication

Supports 3 axioms—when an Actor receives a message it can:

The Actor Model

10

A computational model that embodies:

✓ Processing

✓ Storage

✓ Communication

Supports 3 axioms—when an Actor receives a message it can:

1. Create new Actors

The Actor Model

10

A computational model that embodies:

✓ Processing

✓ Storage

✓ Communication

Supports 3 axioms—when an Actor receives a message it can:

1. Create new Actors

2. Send messages to Actors it knows

The Actor Model

10

A computational model that embodies:

✓ Processing

✓ Storage

✓ Communication

Supports 3 axioms—when an Actor receives a message it can:

1. Create new Actors

2. Send messages to Actors it knows

3. Designate how it should handle the next message it receives

The Actor Model

The essence of an actor from Akka’s perspective

0. DEFINE

1. CREATE

2. SEND

3. BECOME

4. SUPERVISE

11

public class Greeting implements Serializable { public final String who; public Greeting(String who) { this.who = who; }

} !public class Greeter extends AbstractActor {{ receive(ReceiveBuilder. match(Greeting.class, m -> { println(“Hello " + m.who); }). matchAny(unknown -> { println(“Unknown message " + unknown); }).build()); }}

0. DEFINE

12

public class Greeting implements Serializable { public final String who; public Greeting(String who) { this.who = who; }

} !public class Greeter extends AbstractActor {{ receive(ReceiveBuilder. match(Greeting.class, m -> { println(“Hello " + m.who); }). matchAny(unknown -> { println(“Unknown message " + unknown); }).build()); }}

0. DEFINE

12

Define the message(s) the Actor should be able to respond to

public class Greeting implements Serializable { public final String who; public Greeting(String who) { this.who = who; }

} !public class Greeter extends AbstractActor {{ receive(ReceiveBuilder. match(Greeting.class, m -> { println(“Hello " + m.who); }). matchAny(unknown -> { println(“Unknown message " + unknown); }).build()); }}

0. DEFINE

12

Define the message(s) the Actor should be able to respond to

Define the Actor class

public class Greeting implements Serializable { public final String who; public Greeting(String who) { this.who = who; }

} !public class Greeter extends AbstractActor {{ receive(ReceiveBuilder. match(Greeting.class, m -> { println(“Hello " + m.who); }). matchAny(unknown -> { println(“Unknown message " + unknown); }).build()); }}

0. DEFINE

12

Define the message(s) the Actor should be able to respond to

Define the Actor classDefine the Actor’s behavior

ActorSystem system = ActorSystem.create("MySystem"); !ActorRef greeter = system.actorOf(Props.create(Greeter.class), “greeter");

1. CREATE

ActorSystem system = ActorSystem.create("MySystem"); !ActorRef greeter = system.actorOf(Props.create(Greeter.class), “greeter");

1. CREATE

Create an Actor system

ActorSystem system = ActorSystem.create("MySystem"); !ActorRef greeter = system.actorOf(Props.create(Greeter.class), “greeter");

1. CREATE

Create an Actor system

Actor configuration

ActorSystem system = ActorSystem.create("MySystem"); !ActorRef greeter = system.actorOf(Props.create(Greeter.class), “greeter");

Give it a name

1. CREATE

Create an Actor system

Actor configuration

ActorSystem system = ActorSystem.create("MySystem"); !ActorRef greeter = system.actorOf(Props.create(Greeter.class), “greeter");

Give it a name

1. CREATE

Create the Actor

Create an Actor system

Actor configuration

ActorSystem system = ActorSystem.create("MySystem"); !ActorRef greeter = system.actorOf(Props.create(Greeter.class), “greeter");

Give it a name

1. CREATE

Create the ActorYou get an ActorRef back

Create an Actor system

Actor configuration

Guardian System Actor

Actors can form hierarchies

Guardian System Actor

system.actorOf(Props.create(Foo.class), “Foo”);

Actors can form hierarchies

Foo

Guardian System Actor

system.actorOf(Props.create(Foo.class), “Foo”);

Actors can form hierarchies

Foo

Guardian System Actor

context().actorOf(Props.create(A.class), “A”);

Actors can form hierarchies

A

Foo

Guardian System Actor

context().actorOf(Props.create(A.class), “A”);

Actors can form hierarchies

A

B

BarFoo

C

BE

A

D

C

Guardian System Actor

Actors can form hierarchies

A

B

BarFoo

C

BE

A

D

C

Guardian System Actor

Name resolution—like a file-system

A

B

BarFoo

C

BE

A

D

C

/Foo

Guardian System Actor

Name resolution—like a file-system

A

B

BarFoo

C

BE

A

D

C

/Foo

/Foo/A

Guardian System Actor

Name resolution—like a file-system

A

B

BarFoo

C

BE

A

D

C

/Foo

/Foo/A

/Foo/A/B

Guardian System Actor

Name resolution—like a file-system

A

B

BarFoo

C

BE

A

D

C

/Foo

/Foo/A

/Foo/A/B

/Foo/A/D

Guardian System Actor

Name resolution—like a file-system

2. SEND

16

greeter.tell(new Greeting("Charlie Parker”), sender);

2. SEND

16

Send the message asynchronously

greeter.tell(new Greeting("Charlie Parker”), sender);

2. SEND

16

Send the message asynchronously

greeter.tell(new Greeting("Charlie Parker”), sender);

Pass in the sender ActorRef

Bring it together

17

public class Greeting implements Serializable { public final String who; public Greeting(String who) { this.who = who; }

} public class Greeter extends AbstractActor {{ receive(ReceiveBuilder. match(Greeting.class, m -> { println(“Hello " + m.who); }). matchAny(unknown -> { println(“Unknown message " + unknown); }).build()); } }} !ActorSystem system = ActorSystem.create("MySystem"); ActorRef greeter = system.actorOf(Props.create(Greeter.class), “greeter"); greeter.tell(new Greeting(“Charlie Parker”));

3. BECOME

18

public class Greeter extends AbstractActor { public Greeter { receive(ReceiveBuilder. match(Greeting.class, m -> { println(“Hello " + m.who); }). matchEquals(“stop" -> { !!!! }).build(); } }

3. BECOME

18

public class Greeter extends AbstractActor { public Greeter { receive(ReceiveBuilder. match(Greeting.class, m -> { println(“Hello " + m.who); }). matchEquals(“stop" -> { !!!! }).build(); } }

context().become(ReceiveBuilder.

3. BECOME

18

public class Greeter extends AbstractActor { public Greeter { receive(ReceiveBuilder. match(Greeting.class, m -> { println(“Hello " + m.who); }). matchEquals(“stop" -> { !!!! }).build(); } }

Change the behavior

context().become(ReceiveBuilder.

3. BECOME

18

public class Greeter extends AbstractActor { public Greeter { receive(ReceiveBuilder. match(Greeting.class, m -> { println(“Hello " + m.who); }). matchEquals(“stop" -> { !!!! }).build(); } }

Change the behavior

context().become(ReceiveBuilder.match(Greeting.class, m -> {

3. BECOME

18

public class Greeter extends AbstractActor { public Greeter { receive(ReceiveBuilder. match(Greeting.class, m -> { println(“Hello " + m.who); }). matchEquals(“stop" -> { !!!! }).build(); } }

Change the behavior

context().become(ReceiveBuilder.match(Greeting.class, m -> {

println(“Go Away!”);

3. BECOME

18

public class Greeter extends AbstractActor { public Greeter { receive(ReceiveBuilder. match(Greeting.class, m -> { println(“Hello " + m.who); }). matchEquals(“stop" -> { !!!! }).build(); } }

Change the behavior

context().become(ReceiveBuilder.match(Greeting.class, m -> {

println(“Go Away!”);}).build());

3. BECOME

18

public class Greeter extends AbstractActor { public Greeter { receive(ReceiveBuilder. match(Greeting.class, m -> { println(“Hello " + m.who); }). matchEquals(“stop" -> { !!!! }).build(); } }

Change the behavior

context().become(ReceiveBuilder.match(Greeting.class, m -> {

println(“Go Away!”);}).build());

Reactive applications are architected to handle failure at all levels.

Resilient• Failure is embraced as a natural state in the app lifecycle

• Resilience is a first-class construct • Failure is detected, isolated, and managed • Applications self heal

20

“The Typesafe Reactive Platform helps us maintain a very aggressive development and deployment cycle, all in a fail-forward manner.

It’s now the default choice for developing all new services.”  

Peter Hausel, VP Engineering, Gawker Media

Think Vending Machine

Coffee MachineProgrammer

Think Vending Machine

Coffee MachineProgrammer

Inserts coins

Think Vending Machine

Coffee MachineProgrammer

Inserts coins

Add more coins

Think Vending Machine

Coffee MachineProgrammer

Inserts coins

Gets coffee

Add more coins

Think Vending Machine

Coffee MachineProgrammer

Think Vending Machine

Coffee MachineProgrammer

Inserts coins

Think Vending Machine

Coffee MachineProgrammer

Inserts coins

Think Vending Machine

Out of coffee beans error

Coffee MachineProgrammer

Inserts coins

Think Vending Machine

Out of coffee beans errorWrong

Coffee MachineProgrammer

Inserts coins

Think Vending Machine

Coffee MachineProgrammer

Inserts coins

Out of coffee beans

error

Think Vending Machine

Coffee MachineProgrammer

Service Guy

Inserts coins

Out of coffee beans

error

Think Vending Machine

Coffee MachineProgrammer

Service Guy

Inserts coins

Out of coffee beans

error

Adds more beans

Think Vending Machine

Coffee MachineProgrammer

Service Guy

Inserts coins

Gets coffee

Out of coffee beans

error

Adds more beans

Think Vending Machine

The Right Way

ServiceClient

The Right Way

ServiceClient

Request

The Right Way

ServiceClient

Request

Response

The Right Way

ServiceClient

Request

Response

Validation Error

The Right Way

ServiceClient

Request

Response

Validation Error

Application Error

The Right Way

ServiceClient

Supervisor

Request

Response

Validation Error

Application Error

The Right Way

ServiceClient

Supervisor

Request

Response

Validation Error

Application Error

Manages Failure

• Isolate the failure

• Compartmentalize

• Manage failure locally

• Avoid cascading failures

Use Bulkheads

• Isolate the failure

• Compartmentalize

• Manage failure locally

• Avoid cascading failures

Use Bulkheads

Enter Supervision

Enter Supervision

A

B

BarFoo

C

BE

A

D

C

Automatic and mandatory supervisionSupervisor hierarchies

4. SUPERVISE

28

class Supervisor extends UntypedActor { private SupervisorStrategy strategy = new OneForOneStrategy( 10, Duration.create(1, TimeUnit.MINUTES), DeciderBuilder. match(ArithmeticException.class, e -> resume()). match(NullPointerException.class, e -> restart()). matchAny( e -> escalate()). build()); ! @Override public SupervisorStrategy supervisorStrategy() { return strategy; }

Every single actor has a default supervisor strategy. Which is usually sufficient. But it can be overridden.

4. SUPERVISE

28

class Supervisor extends UntypedActor { private SupervisorStrategy strategy = new OneForOneStrategy( 10, Duration.create(1, TimeUnit.MINUTES), DeciderBuilder. match(ArithmeticException.class, e -> resume()). match(NullPointerException.class, e -> restart()). matchAny( e -> escalate()). build()); ! @Override public SupervisorStrategy supervisorStrategy() { return strategy; } ActorRef worker = context.actorOf( Props.create(Worker.class), "worker"); public void onReceive(Object i) throws Exception { … } }

Monitor through Death Watch

29

public class WatchActor extends AbstractActor { final ActorRef child = context().actorOf(Props.empty(), "child"); ! public WatchActor() { context().watch(child); receive(ReceiveBuilder. match(Terminated.class, t -> t.actor().equals(child), t -> { … // handle termination }).build() ); } }

Monitor through Death Watch

29

public class WatchActor extends AbstractActor { final ActorRef child = context().actorOf(Props.empty(), "child"); ! public WatchActor() { context().watch(child); receive(ReceiveBuilder. match(Terminated.class, t -> t.actor().equals(child), t -> { … // handle termination }).build() ); } }

Create a child actor

Monitor through Death Watch

29

public class WatchActor extends AbstractActor { final ActorRef child = context().actorOf(Props.empty(), "child"); ! public WatchActor() { context().watch(child); receive(ReceiveBuilder. match(Terminated.class, t -> t.actor().equals(child), t -> { … // handle termination }).build() ); } }

Create a child actor

Watch it

Monitor through Death Watch

29

public class WatchActor extends AbstractActor { final ActorRef child = context().actorOf(Props.empty(), "child"); ! public WatchActor() { context().watch(child); receive(ReceiveBuilder. match(Terminated.class, t -> t.actor().equals(child), t -> { … // handle termination }).build() ); } }

Create a child actor

Watch it

Handle termination message

Reactive applications scale up and down to meet demand.

Scalable• Scalability and elasticity to embrace the Cloud

• Leverage all cores via asynchronous programming • Clustered servers support joining and leaving of nodes • More cost-efficient utilization of hardware

31

“Our traffic can increase by as much as 100x for 15 minutes each day. Until a couple of years ago, noon was a stressful time.

Nowadays, it’s usually a non-event.”  

Eric Bowman, VP Architecture, Gilt Groupe

Define a router

32

ActorRef router = context().actorOf( new RoundRobinPool(5).props(Props.create(Worker.class)), “router”)

…or from config

33

akka.actor.deployment { /service/router { router = round-robin-pool resizer { lower-bound = 12 upper-bound = 15 } } }

Turn on clustering

34

akka { actor { provider = "akka.cluster.ClusterActorRefProvider" ... }    cluster { seed-nodes = [ “akka.tcp://ClusterSystem@127.0.0.1:2551", “akka.tcp://ClusterSystem@127.0.0.1:2552" ]   auto-down = off } }

Use clustered routers

35

akka.actor.deployment  {      /service/master  {          router  =  consistent-­‐hashing-­‐pool          nr-­‐of-­‐instances  =  100  !        cluster  {              enabled  =  on              max-nr-of-instances-per-node = 3              allow-­‐local-­‐routees  =  on              use-­‐role  =  compute          }      }  }

Use clustered routers

35

akka.actor.deployment  {      /service/master  {          router  =  consistent-­‐hashing-­‐pool          nr-­‐of-­‐instances  =  100  !        cluster  {              enabled  =  on              max-nr-of-instances-per-node = 3              allow-­‐local-­‐routees  =  on              use-­‐role  =  compute          }      }  }

Or perhaps use an AdaptiveLoadBalancingPool

• Cluster Membership • Cluster Pub/Sub • Cluster Leader • Clustered Singleton • Cluster Roles • Cluster Sharding

36

Other Akka Cluster features

• Supports two different models: • Command Sourcing — at least once • Event Sourcing — at most once

• Great for implementing • durable actors • replication • CQRS etc.

• Messages persisted to Journal and replayed on restart

37

Use Akka Persistence

38

Command Sourcing Event Sourcing

38

Command Sourcing Event Sourcing

write-ahead-log

38

Command Sourcing Event Sourcing

write-ahead-log derive events from a command

38

Command Sourcing Event Sourcing

write-ahead-log derive events from a command

same behavior during recovery as normal operation

38

Command Sourcing Event Sourcing

write-ahead-log derive events from a command

same behavior during recovery as normal operation

only state-changing behavior during recovery

38

Command Sourcing Event Sourcing

write-ahead-log derive events from a command

same behavior during recovery as normal operation

only state-changing behavior during recovery

persisted before validation

38

Command Sourcing Event Sourcing

write-ahead-log derive events from a command

same behavior during recovery as normal operation

only state-changing behavior during recovery

persisted before validation events cannot fail

38

Command Sourcing Event Sourcing

write-ahead-log derive events from a command

same behavior during recovery as normal operation

only state-changing behavior during recovery

persisted before validation events cannot fail

allows retroactive changes to the business logic

38

Command Sourcing Event Sourcing

write-ahead-log derive events from a command

same behavior during recovery as normal operation

only state-changing behavior during recovery

persisted before validation events cannot fail

allows retroactive changes to the business logic

fixing the business logic will not affect persisted events

38

Command Sourcing Event Sourcing

write-ahead-log derive events from a command

same behavior during recovery as normal operation

only state-changing behavior during recovery

persisted before validation events cannot fail

allows retroactive changes to the business logic

fixing the business logic will not affect persisted events

naming: represent intent, imperative

38

Command Sourcing Event Sourcing

write-ahead-log derive events from a command

same behavior during recovery as normal operation

only state-changing behavior during recovery

persisted before validation events cannot fail

allows retroactive changes to the business logic

fixing the business logic will not affect persisted events

naming: represent intent, imperative

naming: things that have completed, verbs in past tense

Akka  Persistence  Webinar

Life beyond Distributed Transactions: an Apostate’s Opinion

Position Paper by Pat Helland

“In general, application developers simply do not implement large scalable applications assuming distributed transactions.”  

Pat Helland

http://www-­‐db.cs.wisc.edu/cidr/cidr2007/papers/cidr07p15.pdf

Akka  Persistence  Webinar

Consistency boundary

• Aggregate Root is the Transactional Boundary • Strong consistency within an Aggregate • Eventual consistency between Aggregates

• No limit to scalability

Akka  Persistence  Webinar

Domain Events• Things that have completed, facts

• Immutable

• Verbs in past tense • CustomerRelocated • CargoShipped • InvoiceSent

“State transitions are an important part of our problem space and should be modeled within our domain.”  

Greg Young, 2008

Reactive applications enrich the user experience with low latency response.

Responsive• Real-time, engaging, rich and collaborative

• Create an open and ongoing dialog with users • More efficient workflow; inspires a feeling of connectedness • Fully Reactive enabling push instead of pull

43

“The move to these technologies is already paying off. Response times are down for processor intensive code–such as image

and PDF generation–by around 75%.”  

Brian Pugh, VP of Engineering, Lucid Software

http://reactivemanifesto.org

Typesafe Activatorhttp://typesafe.com/platform/getstarted

Questions?

©Typesafe 2014 – All Rights Reserved