are written for Single machines Clusters of machines Single core processors Multicore processors Expensive RAM Cheap RAM Expensive disk Cheap disk Slow networks
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
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
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
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
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
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
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
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
• 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
✓ 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
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 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 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 Define the Actor’s behavior
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}") } }
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
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
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
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 Actor configuration
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 system Actor configuration
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 system Actor configuration
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 You get an ActorRef back Create an Actor system Actor configuration
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")
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
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")
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
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
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.
{ 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
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") !
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
Sourcing • Great for implementing • durable actors • replication • CQRS etc. • Messages persisted to Journal and replayed on restart 43 Use Akka Persistence
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
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
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
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
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
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
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
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