Building Reactive Systems with Akka (in Java 8 or Scala)
-
Upload
jonas-boner -
Category
Engineering
-
view
23.356 -
download
3
Transcript of Building Reactive Systems with Akka (in Java 8 or Scala)
Building Reactive Systems
with Akka
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
X
Cost Gravity is at Work
X
Reactive Applications
Reactive applications share four traits
5
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
7
“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
Reactive applications react to changes in the world around them.
Message-Driven• Loosely coupled architecture, easier to extend, maintain, evolve
• Asynchronous and non-blocking • Concurrent by design, immutable state • Lower latency and higher throughput
9
“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
11
The Actor Model
11
A computational model that embodies:
The Actor Model
11
A computational model that embodies:
✓ Processing
The Actor Model
11
A computational model that embodies:
✓ Processing
✓ Storage
The Actor Model
11
A computational model that embodies:
✓ Processing
✓ Storage
✓ Communication
The Actor Model
11
A computational model that embodies:
✓ Processing
✓ Storage
✓ Communication
Supports 3 axioms—when an Actor receives a message it can:
The Actor Model
11
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
11
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
11
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
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
X
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
X
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
X
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
X
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
0. DEFINE
13
case class Greeting(who: String) !class GreetingActor extends Actor with ActorLogging { def receive = { case Greeting(who) => log.info(s"Hello ${who}") } }
0. DEFINE
13
Define the message(s) the Actor should be able to respond to
case class Greeting(who: String) !class GreetingActor extends Actor with ActorLogging { def receive = { case Greeting(who) => log.info(s"Hello ${who}") } }
0. DEFINE
13
Define the message(s) the Actor should be able to respond to
case class Greeting(who: String) !class GreetingActor extends Actor with ActorLogging { def receive = { case Greeting(who) => log.info(s"Hello ${who}") } }
Define the Actor class
0. DEFINE
13
Define the message(s) the Actor should be able to respond to
case class Greeting(who: String) !class GreetingActor extends Actor with ActorLogging { def receive = { case Greeting(who) => log.info(s"Hello ${who}") } }
Define the Actor class
Define the Actor’s behavior
case class Greeting(who: String) !class GreetingActor extends Actor with ActorLogging { def receive = { case Greeting(who) => log.info("Hello " + who) } } !val system = ActorSystem("MySystem") val greeter = system.actorOf(Props[GreetingActor], name = "greeter")
1. CREATE
case class Greeting(who: String) !class GreetingActor extends Actor with ActorLogging { def receive = { case Greeting(who) => log.info("Hello " + who) } } !val system = ActorSystem("MySystem") val greeter = system.actorOf(Props[GreetingActor], name = "greeter")
1. CREATE
Create an Actor system
case class Greeting(who: String) !class GreetingActor extends Actor with ActorLogging { def receive = { case Greeting(who) => log.info("Hello " + who) } } !val system = ActorSystem("MySystem") val greeter = system.actorOf(Props[GreetingActor], name = "greeter")
1. CREATE
Create an Actor systemActor configuration
case class Greeting(who: String) !class GreetingActor extends Actor with ActorLogging { def receive = { case Greeting(who) => log.info("Hello " + who) } } !val system = ActorSystem("MySystem") val greeter = system.actorOf(Props[GreetingActor], name = "greeter")
Give it a name
1. CREATE
Create an Actor systemActor configuration
case class Greeting(who: String) !class GreetingActor extends Actor with ActorLogging { def receive = { case Greeting(who) => log.info("Hello " + who) } } !val system = ActorSystem("MySystem") val greeter = system.actorOf(Props[GreetingActor], name = "greeter")
Give it a name
1. CREATE
Create the Actor
Create an Actor systemActor configuration
case class Greeting(who: String) !class GreetingActor extends Actor with ActorLogging { def receive = { case Greeting(who) => log.info("Hello " + who) } } !val system = ActorSystem("MySystem") val greeter = system.actorOf(Props[GreetingActor], name = "greeter")
Give it a name
1. CREATE
Create the ActorYou get an ActorRef back
Create an Actor systemActor 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
Guardian System Actor
Actors can form hierarchies
Guardian System Actor
system.actorOf(Props[Foo], “Foo”)
Actors can form hierarchies
Foo
Guardian System Actor
system.actorOf(Props[Foo], “Foo”)
Actors can form hierarchies
Foo
Guardian System Actor
context.actorOf(Props[A], “A”)
Actors can form hierarchies
A
Foo
Guardian System Actor
context.actorOf(Props[A], “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
X
greeter.tell(new Greeting("Charlie Parker”), sender);
2. SEND
X
Send the message asynchronously
greeter.tell(new Greeting("Charlie Parker”), sender);
2. SEND
X
Send the message asynchronously
greeter.tell(new Greeting("Charlie Parker”), sender);
Pass in the sender ActorRef
Bring it together
X
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”));
2. SEND
17
case class Greeting(who: String) !class GreetingActor extends Actor with ActorLogging { def receive = { case Greeting(who) => log.info(s”Hello ${who}") } } !val system = ActorSystem("MySystem") val greeter = system.actorOf(Props[GreetingActor], name = "greeter") greeter ! Greeting("Charlie Parker")
2. SEND
17
case class Greeting(who: String) !class GreetingActor extends Actor with ActorLogging { def receive = { case Greeting(who) => log.info(s”Hello ${who}") } } !val system = ActorSystem("MySystem") val greeter = system.actorOf(Props[GreetingActor], name = "greeter") greeter ! Greeting("Charlie Parker")
Send the message asynchronously
Bring it together
18
case class Greeting(who: String) !class GreetingActor extends Actor with ActorLogging { def receive = { case Greeting(who) => log.info(s”Hello ${who}") } } !val system = ActorSystem("MySystem") val greeter = system.actorOf(Props[GreetingActor], name = "greeter") greeter ! Greeting("Charlie Parker")
DEMO TIMEA simple game of ping pong
3. BECOME
X
public class Greeter extends AbstractActor { public Greeter { receive(ReceiveBuilder. match(Greeting.class, m -> { println(“Hello " + m.who); }). matchEquals(“stop" -> { !!!! }).build(); } }
3. BECOME
X
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
X
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
X
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
X
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
X
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
X
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
19
class GreetingActor extends Actor with ActorLogging { def receive = happy ! val happy: Receive = { case Greeting(who) => log.info(s”Hello ${who}") case Angry => context become angry } ! val angry: Receive = { case Greeting(_) => log.info("Go away!") case Happy => context become happy } }
3. BECOME
19
class GreetingActor extends Actor with ActorLogging { def receive = happy ! val happy: Receive = { case Greeting(who) => log.info(s”Hello ${who}") case Angry => context become angry } ! val angry: Receive = { case Greeting(_) => log.info("Go away!") case Happy => context become happy } }
Redefine the behavior
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
21
“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
X
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
X
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
X
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
X
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
X
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
X
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
4. SUPERVISE
29
Every single actor has a default supervisor strategy.
Which is usually sufficient. But it can be overridden.
4. SUPERVISE
29
Every single actor has a default supervisor strategy.
Which is usually sufficient. But it can be overridden.
class Supervisor extends Actor { override val supervisorStrategy = OneForOneStrategy(maxNrOfRetries = 10, withinTimeRange = 1 minute) { case _: ArithmeticException => Resume case _: NullPointerException => Restart case _: Exception => Escalate } ! val worker = context.actorOf(Props[Worker], name = "worker") ! def receive = {
4. SUPERVISE
29
class Supervisor extends Actor { override val supervisorStrategy = OneForOneStrategy(maxNrOfRetries = 10, withinTimeRange = 1 minute) { case _: ArithmeticException => Resume case _: NullPointerException => Restart case _: Exception => Escalate } ! val worker = context.actorOf(Props[Worker], name = "worker") ! def receive = { case n: Int => worker forward n } } !
Cleanup & (Re)initialization
30
class Worker extends Actor { ... override def preRestart( reason: Throwable, message: Option[Any]) { ... // clean up before restart } override def postRestart(reason: Throwable) { ... // init after restart } }
Monitor through Death Watch
31
class Watcher extends Actor { val child = context.actorOf(Props.empty, "child") context.watch(child) ! def receive = { case Terminated(`child`) => … // handle child termination } }
Monitor through Death Watch
31
class Watcher extends Actor { val child = context.actorOf(Props.empty, "child") context.watch(child) ! def receive = { case Terminated(`child`) => … // handle child termination } }
Create a child actor
Monitor through Death Watch
31
class Watcher extends Actor { val child = context.actorOf(Props.empty, "child") context.watch(child) ! def receive = { case Terminated(`child`) => … // handle child termination } }
Create a child actor
Watch it
Monitor through Death Watch
31
class Watcher extends Actor { val child = context.actorOf(Props.empty, "child") context.watch(child) ! def receive = { case Terminated(`child`) => … // handle child termination } }
Create a child actor
Watch it
Handle termination message
Reactive applications scale up and down to meet demand.
Elastic• Elasticity and Scalability to embrace the Cloud
• Adaptive Scale on Demand • Clustered servers support joining and leaving of nodes • More cost-efficient utilization of hardware
33
“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
34
Scale OUTScale UP
34
Essentially the same thing
35
1. Minimize Contention 2. Maximize Locality of Reference
We need to
36
Share NOTHING
Design
Fully event-driven apps are a necessity
X
Amdahl’s Law will hunt you down
Define a router
X
ActorRef router = context().actorOf( new RoundRobinPool(5).props(Props.create(Worker.class)), “router”)
Define a router
37
val router = context.actorOf( RoundRobinPool(5).props(Props[Worker])), “router”)
…or from config
38
akka.actor.deployment { /service/router { router = round-robin-pool resizer { lower-bound = 12 upper-bound = 15 } } }
Turn on clustering
39
akka { actor { provider = "akka.cluster.ClusterActorRefProvider" ... } cluster { seed-nodes = [ “akka.tcp://[email protected]:2551", “akka.tcp://[email protected]:2552" ] auto-down = off } }
Use clustered routers
40
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
40
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
Use clustered pub-sub
41
Use clustered pub-sub
41
class Subscriber extends Actor { val mediator = DistributedPubSubExtension(context.system).mediator mediator ! Subscribe(“content”, self) def receive = { … } }
Use clustered pub-sub
41
class Publisher extends Actor { val mediator = DistributedPubSubExtension(context.system).mediator def receive = { case in: String => mediator ! Publish("content", in.toUpperCase) } }
• Cluster Membership • Cluster Pub/Sub • Cluster Leader • Clustered Singleton • Cluster Roles • Cluster Sharding
42
Other Akka Cluster features
• Supports two different models:
• Command Sourcing
• Event Sourcing
• Great for implementing
• durable actors
• replication
• CQRS etc.
• Messages persisted to Journal and replayed on restart
43
Use Akka Persistence
X
Command Sourcing Event Sourcing
X
Command Sourcing Event Sourcing
write-ahead-log
X
Command Sourcing Event Sourcing
write-ahead-log derive events from a command
X
Command Sourcing Event Sourcing
write-ahead-log derive events from a command
same behavior during recovery as normal operation
X
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
X
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
X
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
X
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
X
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
X
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
X
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
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
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
• An Actor is can define an Aggregate Root • Each containing one or more Entities
• Aggregate Root is the Transactional Boundary • Strong consistency within an Aggregate • Eventual consistency between Aggregates
• No limit to scalability
DEMO TIMEPersist a game of ping pong
http://reactivemanifesto.org
Typesafe Activatorhttp://typesafe.com/platform/getstarted
48
Typesafe Reactive Platform
• Actors are asynchronous and communicate via message passing
• Supervision and clustering in support of fault tolerance
• Purely asynchronous and non-blocking web frameworks
• No container required, no inherent bottlenecks in session management
• Asynchronous and immutable programming constructs
• Composable abstractions enabling simpler concurrency and parallelism
Reactive is being adopted acrossa wide range of industries.
50
Finance Internet/Social Media Mfg/Hardware Government Retail
Questions?
©Typesafe 2014 – All Rights Reserved