backday xebia : akka, the reactive toolkit
TRANSCRIPT
#backdaybyxebia
FABIAN GUTIERREZXAVIER BUCCHIOTTY
Construire le SI de demain
Akka, the reactive toolkit
#backdaybyxebia
Agenda● Concepts● Reactive Application● Actor Basics● Actor Supervision● Scale OUT● Use case
#backdaybyxebia
Concurrency"Two or more tasks are making progress even though they might not be executing simultaneously"
Parallelism"The execution of multiple things is truly simultaneous"
Rob Pike - 'Concurrency Is Not Parallelism': https://vimeo.com/49718712
#backdaybyxebia
Synchronous"A method call is considered synchronous if the caller cannot make progress until the method returns a value or exception"
Asynchronous"Async call allows the caller to progress after a finite number of steps and the completion may be signaled via a callback, Future or message"
#backdaybyxebia
Blocking"When the delay of one thread can indefinitely delay some of the other threads"
Non blocking"No thread is able to indefinitely delay others"
#backdaybyxebia
RUsers = REvents + RLoad + RFailure
React to events, pushing rather than pullingReact to load, focus on scalability rather than single-user performanceReact to failure, resilient systems with the ability to recover quickly at all levels
#backdaybyxebia
- Áhkká
All the wisdom and beauty of the world according to the sami mythology
A mountain in Laponia in the north part of Sweden
#backdaybyxebia
ProblemIt is very difficult to build:
● Correct highly concurrent systems
● Truly scalable systems● Self-healing, fault-
tolerant systems
GoalMake simpler:
● Concurrency● Scalability● Fault-tolerance
#backdaybyxebia
A computational model that embodies:● Processing● Storage● Communication
Actor Model
Carl Hewitt
#backdaybyxebia
"The Actor model allows the developer to write concurrent and distributed systems by abstracting the low-level problems of having to deal with locking and thread management"
Actor Model
#backdaybyxebia
● Actors have a mailbox, all messages received go there
● When a message is received they decide what to do based on message type and content
Actor Behaviour● In addition to normal
operations, they can create new actors or send messages to other actors
● Messages can only be sent to actors whose address is known
#backdaybyxebia
Defining Actorscase object Tick
class MyCounterActor extends Actor{ var counter = 0
def receive = { case Tick => counter += 1 println(counter) }}
val config = ConfigFactory.
load()
val system = ActorSystem("system", config)
val counterRef = system.actorOf(
Props[MyCounterActor],
"MyActor")
#backdaybyxebia
Sending Messages to ActorscounterRef ! Tick
counterRef tell Tick
counterRef.tell(Tick)
counterRef.tell(Tick, senderRef)
implicit val timeout =
Timeout(5 seconds)
val future = actor ? message//orval future = ask (actorRef, Tick, 5 seconds)
#backdaybyxebia
Become / Unbecomeclass HotSwapActor extends Actor with Stash { import context._ def angry: Receive = { case "foo" => sender() ! "I am already angry?" case "bar" => become(happy) }
def happy: Receive = { case "bar" => sender() ! "I am already happy :-)" case "foo" => become(angry) }
def receive = { case "foo" => become(angry) case "bar" => become(happy) }}
The Stash trait enables an actor to temporarily stash away messages that can not or should not be handled using the actor's current behavior
#backdaybyxebia
Actor Selectionval actorRef = system.actorFor( "/MyActor/A")
val parent = system.actorFor( "..")
val sibling = system.actorFor( "../B")
val refPath: ActorPath = actorRef.path
val selection =
system.actorSelection( "/MyActor/*")
selection ! "hello there"
#backdaybyxebia
The Guardians = Top level Supervisors
/user, the guardian actorParent of all user-created actors
/system, the system guardianAllows an ordered shut-down sequence
/, the root guardianSupervises all top level and special actors
/user /system
/ You don’t interact with this other root actors as a user
Root Actor
#backdaybyxebia
● When?the actor is in corrupt internal state
Restarting
● How is it done?By creating a new instance of the actor and keeping the previous mailbox
#backdaybyxebia
Lifecycle monitoring or DeathWatch● An actor can monitor any other actor in order to
react to his termination
● It means that supervision reacts to failure
● Each actor is the supervisor of its children, and as such each actor defines fault handling strategy
#backdaybyxebia
Fault Tolerance(...or let it crash)
When an actor fails…
the supervisor will know what to do!!
#backdaybyxebia
Default Supervision (in parent actors)final val defaultStrategy: SupervisorStrategy = {
def defaultDecider: Decider = {
case _: ActorInitializationException => Stop
case _: ActorKilledException => Stop
case _: Exception => Restart
}
OneForOneStrategy()(defaultDecider)
}
You don’t have to specify your own supervisor strategy in each and every actor.
However, the default strategy looks like this:
#backdaybyxebia
Defining a supervisor strategyclass MyActorSupervisor extends Actor {
override val supervisorStrategy = OneForOneStrategy ( maxNrOfRetries = 10, withinTimeRange = 1 minute) { case _: ArithmeticException => Resume case _: NullPointerException => Restart case _: IllegalArgumentException => Stop case _: Exception => Escalate }
}
#backdaybyxebia
No idea what to do?Ask your supervisor!!
override val supervisorStrategy = OneForOneStrategy ( maxNrOfRetries = 10, withinTimeRange = 1 minute) { case _: ArithmeticException => Resume case t => super.supervisorStrategy.decider .applyOrElse(t,(_: Any) => Escalate) }
One-For-One All-For-One
OneForOneStrategy AllForOneStrategy
Default strategy Recommended when there are tight dependencies among children
Applies directives only to the failed child Applies directives to the failed child and siblings
#backdaybyxebia
Manage Failure From Within
class FaultTolerantService extends Actor {
override def preRestart( reason: Throwable, message: Option[Any]) = { // clean up before restart }
override def postRestart(reason: Throwable) = { // init after restart } ...}
Lifecycle hooks:● preStart● postStop● preRestart● postRestart
#backdaybyxebia
RoutingRedirects messages● RoundRobin● Random● SmallestMailbox● Broadcast● custom
val routerRef = system.actorOf(Props[Counter].withRouter( RoundRobinRouting( nrOfInstances = 5)))
akka.actor.deployment { /parent/router1 { router = round-robin-pool nr-of-instances = 5 }}
#backdaybyxebia
Remote communicationIf no deployment configuration exist then the actor is deployed as local...it implies:● Write as local● Deploy as distributed in the cloud● No code change
#backdaybyxebia
Akka RemoteCommunication between systems is symmetricif a system A can connect to a system B then system B must also be able to connect to system A independently
The role of the communicating systems are symmetric in regards to connection patternsThere is no system that only accepts connections, and there is no system that only initiates connections
#backdaybyxebia
akka { actor { provider = "akka.remote.RemoteActorRefProvider" } remote { enabled-transports = [ "akka.remote.netty.tcp"] netty.tcp { hostname = "127.0.0.1" port = 2552 } }}
val selection = context.actorSelection( "akka.tcp://[email protected]:2552/user/MyActor")
selection ! "Pretty awesome feature"
#backdaybyxebia
Akka Clustering● Fault-tolerant● Decentralized● Peer-to-Peer
Cluster membership service no single point of failure or single point of bottleneck
#backdaybyxebia
Akka Clustering/producer/router { router = round-robin nr-of-instances = 100 cluster { enabled = on routees-path = "/user/routee" allow-local-routees = on }}
#backdaybyxebia
Failure Detection● Hashes the node ring● Picks 5 nodes● Request/Reply
heartbeat
Network Partition● Failure detector can mark an
unavailable member unreachable● If one node is unreachable then
no cluster convergence● This means that the leader
cannot perform its duties● A member can become
reachable again
#backdaybyxebia
Big Questions!● Testing async calls without
using threads explicitly● You are not Brian Goetz and you
want to use ThreadLocals● CountDownLatch is not enough
#backdaybyxebia
Enter Specsclass MyEchoSpec(_system: ActorSystem) extends TestKit(_system) with ImplicitSender with WordSpecLike with Matchers with BeforeAndAfterAll {
def this() = this(ActorSystem("MySpec"))
override def afterAll { TestKit.shutdownActorSystem (system) }
"An Echo actor" must { "send back messages unchanged" in { val echo = system.actorOf(TestActors.echoActorProps) echo ! "hello world" expectMsg("hello world") } } }
#backdaybyxebia
Built-In AssertionsexpectMsg[T](d: Duration, msg: T): TexpectNoMsg(d: Duration)
expectMsgClass[T](d: Duration, c: Class[T]): TexpectMsgType[T: Manifest](d: Duration)
expectMsgAnyOf[T](d: Duration, obj: T*): TexpectMsgAnyClassOf[T](d: Duration, obj: Class[_ <: T]*): TexpectMsgAllOf[T](d: Duration, obj: T*): Seq[T]
receiveN(n: Int, d: Duration): Seq[AnyRef]
#backdaybyxebia
Multi-JVM"form a cluster" in { runOn(ClusterNodes: _*) { SeedDiscovery.joinCluster(system) enterBarrier( "deployed") }}
"support network partition" in { runOn(node1) { for { from <- group_1 to <- group_2 } { testConductor. blackhole(from, to, Direction.Both).await } }}
#backdaybyxebia
TCP Server DB
session2
session1
create create
is injected in
uses
Actors can create other actors and supervise them.
#backdaybyxebia
TCP Server DB
session2
session1
create create
is injected in
uses
Actors process one message at a time.
#backdaybyxebia
TCP Server
DBrouter
session2
session1
create
is injected in
Actors can be behind routers to scale UP.
conn1
conn2
create
uses
#backdaybyxebia
TCP Server
DBrouter
session2
session1
create
is injected in
conn1
conn2
create
uses
Actors can create other actors and supervise them.
#backdaybyxebia
TCP Server
DBrouter
session2
session1
create
is injected in
conn1
conn2
create
uses
Actors can communicate with other actors.
Alertingis injected in
#backdaybyxebia
TCP Server
DBrouter
session2
session1
create
is injected in
conn1
conn2
create
uses
Actors can be deployed remotely to scale OUT.
Alertingis injected in
#backdaybyxebia
Alerting
TCP Server
DBrouter
session2
session1
create
is injected in
conn1
conn2
create
uses
Actors can be deployed remotely to scale OUT.
Alertingare injected in