1 / 85

Advanced T opics in Functional and Reactive Programming: Actor Model & Akka

Advanced T opics in Functional and Reactive Programming: Actor Model & Akka. Majeed Kassis. Threads. Waits. Locks. Shared Memory. Thread. Thread. Dead Lock !. Shared Memory. Waits. Locks. Threads are used to execute code concurrently Threads contains resources that they are shared

cainsworth
Télécharger la présentation

Advanced T opics in Functional and Reactive Programming: Actor Model & Akka

An Image/Link below is provided (as is) to download presentation Download Policy: Content on the Website is provided to you AS IS for your information and personal use and may not be sold / licensed / shared on other websites without getting consent from its author. Content is provided to you AS IS for your information and personal use only. Download presentation by click this link. While downloading, if for some reason you are not able to download a presentation, the publisher may have deleted the file from their server. During download, if you can't get a presentation, the file might be deleted by the publisher.

E N D

Presentation Transcript


  1. Advanced Topics in Functional and Reactive Programming:Actor Model & Akka MajeedKassis

  2. Threads Waits Locks Shared Memory Thread Thread Dead Lock ! Shared Memory Waits Locks • Threads are used to execute code concurrently • Threads contains resources that they are shared • Synchronization help guarantee: • Correct ordering • Visibility • Data consistency • Problems? • Hard to solve deadlocks • Hard to maintain data consistency • Hard to ensure liveness

  3. Thread-Based Concurrency Issues • No fault tolerance by default • Programmers need to think of all extreme failure cases! • Need to handle them one by one. • Nondeterminism: • Unpredictable thread scheduling • The increasing use of Arbiters which controls concurrent access to memory • Snapshotting and replication • Global state – snapshots – complex to generate! • data replication is hard due to shared state!

  4. Actor Model – Asynchronous Concurrency • General purpose concurrent programming model • Facilitates geographical distribution • Strong support for fault tolerance • Allows mutable state, but avoids sharing it. • Actor: The fundamental unit of concurrent computation in the model. • Can be implemented as processes or threads, or any other entity. • Benefits: • Simple and high-level abstractions for distribution, concurrency and parallelism. • Asynchronous, non-blocking and highly performant message-driven programming model. • Very lightweight event-driven processes (several million actors per GB of heap memory).

  5. An Actor Unit Consists of Three Things Mailbox Mailbox Behavior State Message Message Message • Processing: “Behavior” • To get something done • Same as objects: Methods • Storage: “State” • To be able to remember/store things • Same as objects: Fields • Communication: “Messages” via “MailBoxes” • To exchange information between actors • Different than objects: Real communication instead of method calling

  6. Actor Model – Key Principles • No shared state between actors! • Not need for synchronization • Each Actor has his own mail-box(es) • Used as a buffer for incoming messages • Messages are handled sequentially • Messages are passed asynchronously • The receives does not need to immediately handle the sent message • Actors can create new Actors • No single ‘manager’ of the system.

  7. Messages in Actor Model • Actors exchange information by sending messages. • Messages are sent asynchronously • Receiving Actors do not block waiting for a message. • Messages are stored in an Actor ‘mail-box’ (called ‘channel’ as well) • A queue with multiple producers – other actors sending messages • And a single consumer – the actor handles these messages sequentially • Messages are immutable! • They can be handled by the actor • Or, they can be propagated as is – without modification

  8. Reactions Upon Message Receive • Actor can send a finite number of messages to other actors • Actors may have multiple mail-boxes • Actors are addressed • Can have no address, one address, or multiple addresses • Actor can spawn a finite number of new actors • Actor change its own internalbehavior • This occurs when a control message is received • Takes effect when the next incoming message is handled! • Behavior is stacked and can be popped or pushed as needed

  9. Fault Tolerance • Uses “let is crash” philosophy • Instead of programming defensively, allow critical code to crash! • Once crashed, a monitoring process receives a message and it needs: • Message is received from a supervisor Actor • To restore the state to a stable one: • Stop or restart crashed actor • Allows creating “self-healing” systems! • Implementation Example • Each Actor code is ran in a process, isolated, with their own state • Monitoring process acts as a supervisor • Receives message on Actor crash • Restores to safe state, can be done via rollback partial changes • Restart crashed Actor, mostly with initial state • Can also stop actor, or delegate problem upward

  10. Actor Model Characteristics • Messages are sent asynchronously • They can take arbitrarily long to eventually arrive in the mailbox of the receiver. • There is no guarantees on the ordering of messages. • Queuing and de-queuing of messages in a mailbox are atomicoperations • There cannot be a race condition. • Incoming messages are processed sequentially

  11. Actor Model Characteristics • Reacting • Actor changes its own internal behavior, allows to deal with mutablestate • The new behavior is applied after the current message has been handled • Every message handling run still represents a side-effect free operation from a conceptual perspective • Allows modelling inherently concurrent systems • Each actor is entirely independent of any other instances. • There is no shared state – no sharing of references or pointers using messages • Interaction between actors is purely based on asynchronous messages

  12. Message Passing Implementations • Most implementations ensure that two messages sent from one actor to another maintain their order at arrival. • Messaging is always asynchronous: • tell(): Send request, no need for response [Fire-Forget] • ask(): Send request, expect response [Ask • forward(): Send request asking it to be forwarded to 3rd party. • Messages: • Can be any object • Should be immutable • Should be serializable

  13. Message Handling Implementations Most implementations provide pattern matching. This enables the developer to distinguish between different types of messages and supply different handling code associated to the type of message. While receiving messages is a blocking operation from the actor's perspective, sending new messages is always non-blocking. Some implementations also provide selective receive semantics. Depending on its behavior, an actor may then wait for a specific message in the mailbox and temporarily defer others.

  14. Actor Model Programing Languages • Erlang: • Programming language based on actor model • Introduced in 1986 by Ericsson • https://www.erlang.org/ • Elixir: • Wrapper of Erlanglanguage • Dynamic, functional language designed for building scalable and maintainable applications. • By Plataformatic, in 2011 • http://elixir-lang.org/

  15. Actor Model Libraries • Akka: • Apache’s toolkit and runtime for building highly concurrent, distributed, and resilient message-driven applications on the JVM. • By Lightbend in 2009 • http://akka.io • C++ Actor Framework: CAF_C++ • Implemented in C++11 • Uses Work-Stealing Scheduling • Lockfree implementation of mailboxes • using CAS operation for enqueue/dequeue • Presented in 2011, open source! • http://actor-framework.org/

  16. Akka Mailboxes • Akka toolkit has many types of mailboxes: • Blocking and non-blocking queues • Bounded and non-bounded queues • Priority queues • Control aware queues • Akka also allows making custom defined queues. • By implementing “MailBoxType” interface • You can implement your own priority queue!

  17. Akka: Built-In MailBox Types • UnboundedMailbox • SingleConsumerOnlyUnboundedMailbox • NonBlockingBoundedMailbox • UnboundedControlAwareMailbox • UnboundedPriorityMailbox • UnboundedStablePriorityMailbox • BoundedMailbox • BoundedPriorityMailbox • BoundedStablePriorityMailbox • BoundedControlAwareMailbox

  18. Actor: 4 Core Operations • Define: Implement a new Actor • Implements the behavior of the actor: how to handle message receive • Create: Instantiate a new Actor • Send: • Send a message to an Actor • Three kinds: • Fire-Forget, send a message asynchronously and return immediately • Ask-Reply, sends a message asynchronously and returns a Future representing a possible reply • Forward, forward a message from one actor to another • Become: • Change the behavior (algorithm) of an Actor dynamically! • This takes effect beginning from the next message onward. • Supervise: • Manage another Actor failure

  19. Creating an Actor finalActorRefprinterActor=system.actorOf(Printer.props(),"printerActor"); finalActorRefhowdyGreeter=system.actorOf(Greeter.props("Howdy",printerActor),"howdyGreeter"); finalActorRefhelloGreeter=system.actorOf(Greeter.props("Hello",printerActor),"helloGreeter"); finalActorRefgoodDayGreeter=system.actorOf(Greeter.props("Good day",printerActor),"goodDayGreeter"); • Each Actor is represented by an ActorRef • You never get access to an Actor instance • An Actor reference allows sending messages to an Actor • Receving is done in asynchronous manner!

  20. Actor Hierarchies • Actors can form hierarchies. • Foo is a base actor

  21. Actor Hierarchies • Actors can form hierarchies. • A is a child actor of Foo

  22. Actors Name Resolution

  23. Actor Hierarchy: Child Actors • The use of child actors • Delegate work to a child actor which increases resiliency and responsiveness • A child actor is created to do the handling of a specific event • The result is forwarded to the original sender, not the parent actor • Managing Exceptions • Actors can manage exceptions thrown by child actors • SupervisorStrategy is created to map each exception with its handling • Can be used to stop or restart child

  24. Hierarchy Akka SystemDefault Actor akka://application/user ActorRef supervisor =Akka.system().actorOf(Props.create(SupervisorActor.class),“Supervisor”); SupervisorActor parent akka://application/user/Supervisor ActorRef child =getContext().actorOf(Props.create(ChildActor.class),“Child”); ChildActor akka://application/user/Supervisor/Child child Actors can form hierarchies

  25. Actor Hierarchy

  26. Actor Reference • An actor reference is a subtype of ActorRef • Its main purpose is to support sending messages to the actor it represents. • Each actor has access to its canonical (local) reference through the self field • this reference is also included as sender reference by default for all messages sent to other actors. • During message processing the actor has access to a reference representing the sender of the current message through the sender() method. • Two general categories for obtaining Actor Referneces: • creating actors - using ActorSystem.actorOf() • looking up actors – using ActorSystem.actorSelection()

  27. Actor Path • Actors are created in a hierarchical fashion • There exists a unique sequence of actor names given by recursively following the supervision links between child and parent down towards the root of the actor system. • This sequence can be seen as enclosing folders in a file system, hence we adopted the name “path” to refer to it, although actor hierarchy has some fundamental difference from file system hierarchy. • An actor path consists of: • An anchor, which identifies the actor system • Followed by the concatenation of the path elements, • From root guardian to the designated actor • The path elements are the names of the traversed actors and are separated by slashes.

  28. Actor Path: Example

  29. Actor Path Anchors "akka://my-sys/user/service-a/worker1" // purely local "akka.tcp://my-sys@host.example.com:5678/user/service-b" // remote • Each actor path has an address component • describing the protocol and location by which the corresponding actor is reachable • followed by the names of the actors in the hierarchy from the root up • Examples:

  30. Defining Greeter Actor: 1 publicclassGreeterextendsAbstractActor{ staticpublicProps props(String message,ActorRefprinterActor){ returnProps.create(Greeter.class,()->newGreeter(message,printerActor)); } staticpublicclassWhoToGreet{ publicfinalString who; publicWhoToGreet(String who){this.who= who;} } staticpublicclassGreet{ publicGreet(){} }

  31. Defining Greeter Actor: 2 privatefinalString message; privatefinalActorRefprinterActor; privateString greeting =""; publicGreeter(String message,ActorRefprinterActor){ this.message= message; this.printerActor=printerActor; } @OverridepublicReceivecreateReceive(){ returnreceiveBuilder() .match(WhoToGreet.class,wtg->{this.greeting= message +", "+wtg.who;}) .match(Greet.class, x ->{printerActor.tell(newGreeting(greeting),getSelf()); }).build(); } }

  32. Defining Printer Actor: publicclassPrinterextendsAbstractActor{ staticpublicProps props(){ returnProps.create(Printer.class,()->newPrinter()); } staticpublicclassGreeting{ publicfinalString message; publicGreeting(String message){this.message= message;} } privateLoggingAdapter log =Logging.getLogger(getContext().getSystem(),this); publicPrinter(){} @Override publicReceivecreateReceive(){ returnreceiveBuilder() .match(Greeting.class, greeting ->{ log.info(greeting.message); }).build(); } }

  33. Creating a new Actor with a non-default MailBox type publicclassHelloActorextends Controller { publicstatic Result index(){ • ActorRef greeter =Akka.system().actorOf(Props.create(GreeterActor.class, “Greeter”) • .withMailBox(“akka.dispatch.UnboundedMailbox”)); return ok("ok"); } }

  34. Sending Messages • Sending a message is done by using the destination actor reference • Messages in Actor Model are addressed • Types of messages: • Tell: Fire and Forget [Asynchronous and Non-blocking] • Ask: Ask and Reply [Blocking] • Forward: Forwarding the received message • Messages must be immutable!

  35. Sending a message to an Actor package controllers; publicclassHelloActorextends Controller { publicstatic Result index(){ ActorRef actor =Akka.system().actorOf(Props.create(Greeter.class, “Greeter”)); • actor.tell("Hello Actor!!",null); //send a message, no reply expected! return ok("ok"); } } Akka.system() – Creates a new system Props.create() – Creates a new configuration

  36. Target Tell A B message Target.tell(message,sender); ActorRef ActorRef Object To send a message to an actor, you need an Actor reference Asynchronous and Non-blocking (Fire-and-forget) null ActorRef.noSender() getSelf() …

  37. Target Tell A B message Target.tell(message,sender); • publicvoidonReceive(Object message){ • if(message instanceof String){ • System.out.println((String) message); • System.out.println("getSender()="+ getSender()); • } • }

  38. Target Tell A B message Target.tell(message,sender); ActorRef ActorRef Object EXAMPLE: B.tell(“Hello Actor”,ActorRef.noSender()); B.tell(new Person(“David”,”Chang”),getSelf());

  39. Target Ask message A B reply 1 Future<Object> rt=Patterns.ask(Target, message,timeout); 2 getSender().tell(reply_message,getSelf()); 3 String result=Await.result(rt, timeout.duration);

  40. Ask publicclassHelloActorextends Controller { publicstatic Result index(){ ActorRef actor =Akka.system().actorOf(Props.create(AnActor.class)); final Timeout timeout=new Timeout(Duration.create(1, SECONDS)); Future<Object>rt=Patterns.ask(actor,"What's your name?", timeout); try{ String result =(String)Await.result(rt,timeout.duration()); System.out.println("The name is "+result); return ok("The name is "+result); }catch(Exception e){ System.out.println(e); } return ok(""); } }

  41. Ask importakka.actor.UntypedActor; importakka.japi.Procedure; publicclassAnActorextendsUntypedActor{ publicvoidonReceive(Object message){ if(message.equals("What's your name?")){ getSender().tell("David",getSelf()); • }else • if(message instanceof String){ • System.out.println((String) message); }else{ System.out.println("Unhandled message"); } } }

  42. Target Forward A B C forward Target.forward(message,getContext()); ActorContext Target.tell(message,getSender()); ActorRef

  43. Back to Example: Executing publicclassDriver { publicstaticvoid main(String[]args){ finalActorSystem system =ActorSystem.create("helloakka"); try{ • finalActorRefprinterActor=system.actorOf(Printer.props(),"printerActor"); • finalActorRefhowdyGreeter=system.actorOf(Greeter.props("Howdy",printerActor),"howdyGreeter"); • finalActorRefhelloGreeter=system.actorOf(Greeter.props("Hello",printerActor),"helloGreeter"); • finalActorRefgoodDayGreeter=system.actorOf(Greeter.props("Good day",printerActor),"goodDayGreeter"); • howdyGreeter.tell(newWhoToGreet("Akka"),ActorRef.noSender()); • howdyGreeter.tell(newGreet(),ActorRef.noSender()); • howdyGreeter.tell(newWhoToGreet("Lightbend"),ActorRef.noSender()); • howdyGreeter.tell(newGreet(),ActorRef.noSender()); • helloGreeter.tell(newWhoToGreet("Java"),ActorRef.noSender()); • helloGreeter.tell(newGreet(),ActorRef.noSender()); • goodDayGreeter.tell(newWhoToGreet("Play"),ActorRef.noSender()); • goodDayGreeter.tell(newGreet(),ActorRef.noSender()); • }catch(IOExceptionioe){}finally{system.terminate();}}}

  44. Become/Unbecome • Akka supports hotswapping the Actor’s behavior at runtime: • Invoke the context.become() method from within the Actor. • become takes a PartialFunction<Object, BoxedUnit> • It implements the new message handler. • Become Variants: • The hotswapped code is kept in a Stack which can be pushed and popped. • Unbecome is used to revert to previous behavior • The hotswapped code is replaced completely, by a new behavior. • Unbecome is never used. Become explicitly defines a new behavior • Note: • The actor will revert to its original behavior when restarted by its Supervisor

  45. Become: Explicit Behavior Change staticclassHotSwapActorextendsAbstractActor{ privateAbstractActor.Receive angry; privateAbstractActor.Receive happy; publicHotSwapActor(){ angry =receiveBuilder() .matchEquals("foo", s ->{getSender().tell("I am already angry?",getSelf());}) .matchEquals("bar", s ->{getContext().become(happy);}) .build(); happy =receiveBuilder() .matchEquals("bar", s ->{getSender().tell("I am already happy :-)",getSelf());}) .matchEquals("foo", s ->{getContext().become(angry);}) .build(); } publicReceivecreateReceive(){ returnreceiveBuilder() .matchEquals("foo", s ->getContext().become(angry)) .matchEquals("bar", s ->getContext().become(happy)) .build(); } }

  46. Top-Level Supervisors: /user - The Guardian Actor • Actors created using system.actorOf() are children of this actor. • When this guardian terminates • all normal actors in the system will be shutdown as well! • Its supervisor strategy determines how the top-level normal actors are supervised • When the guardian escalates a failure • The root guardian’s response will be to terminate the user guardian • Which in effect will shut down the whole actor system.

  47. Top-Level Supervisors: /system - The System Guardian • Used to allow an orderly shut-down sequence • Where logging remains active while all normal actors terminate • It initiates its own shut-down upon reception of the Terminated message. • Terminated message is received from /user guardian upon system shutdown. • The top-level system actors are supervised using a strategy which will restart indefinitely upon all types of Exception • ActorInitializationExceptionand ActorKilledException, • will terminate the child in question.

  48. Top-Level Supervisors: / - The Root Guardian • The grand-parent of all top-level actors • Supervises all the special actors mentioned in Top-Level Scopes for Actor Paths • Uses the SupervisorStrategy.stoppingStrategyas a supervision strategy • SupervisorStrategy.stoppingStrategy • Used to terminate the child upon any type of Exception. • Other types of throwables will be escalated to the system • System’s isTerminated set to true once root guardian is fully terminated • All children recursively stopped

  49. Supervise – Fault Tolerance • SUPERVISE (monitor): manage another Actor’s failures • Error handling in actors is handle by letting Actors monitor each other for failure • This means that if an Actor crashes, a notification will be sent to his supervisor, who can react upon the failure: • Stop - The stream is completed with failure • Resume - The element is dropped and the stream continues • Restart - • The element is dropped and the stream continues after restarting the stage. • Restarting a stage means that any accumulated state is cleared. • This is typically performed by creating a new instance of the stage. • Every single actor has a default supervisor strategy. • Which is usually sufficient • But it can be overridden

  50. Setting Up Akka • Getting Started: • http://doc.akka.io/docs/akka/current/intro/getting-started.html • Prerequisites/Download/IDE Integration • Using Akka with Maven or SBT • HelloActorTutorial: • https://www.playframework.com/documentation/2.5.x/JavaAkka • Complete Akka Guide: • http://doc.akka.io/docs/akka/2.5.0/AkkaJava.pdf

More Related