Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Reaktive Applikationen mit Scala, Play und Akka

Reaktive Applikationen mit Scala, Play und Akka

Dieser Vortrag stellt mit Play2, Scala und Akka einen Technologiestack vor, der es ermöglicht relativ einfach eine skalierbare Webarchitektur aufzubauen, die mit dem Web und nicht gegen das Web arbeitet. Insbesondere der Einsatz von Akka als flexible Messagingplattform bietet dabei einige Vorteile gegenüber konventionellen Thread basierten Lösungen in Bezug auf die horizontale Skalierung der Applikation.

Avatar for Markus Klink

Markus Klink

November 08, 2013

Other Decks in Technology

Transcript

  1. Reactive Applications • ereignisgetrieben! • skalierbar! • widerstandsfähig! • reaktionsschnell

    • HTML5/Javascript / Akteure! • zustandslos! • fehlerresistent / ausfallsicher http://reactivemanifesto.org
  2. Failures & Scalability Hardware Software Network DAU • Dinge scheitern!

    • Scaling: Je mehr Dinge wir haben, desto mehr wird scheitern! • Scheitern muss in das System eingebaut sein => Resilient Systems ermöglichen es zu skalieren
  3. Play • Asynchron, non blocking! • keine Abstraktion über HTTP

    Standard! • JSON friendly! • zustandslos! • Akka built in! • Websockets, Eventsource, Comet, …
  4. Fallbeispiel ! • 3 Rollen: Client, Master, Server! • interne

    Kommunikation über Akteure! • externe Kommunikation über Akteure und Rest/Webservices
  5. Was ist ein Akteur? Akteure kapseln Zustand, Verhalten, besitzen eine

    Mailbox und bilden Hierarchien.! ! Akteure kommunizieren über Nachrichten.! !
  6. Codebeispiel class ImageActor(id: String, directoryActorRef: ActorRef) extends Actor {! !

    implicit val ec = context.dispatcher! ! val ticker = context.system.scheduler.scheduleOnce(3.minutes, self, ImageActor.TimeOutImageEvaluation)! ! def receive = {! case ImageActor.Evaluation(_, tags) => ! ticker.cancel! log.info(s"received tags $tags for actor $self")! sender ! DirectoryActor.EvaluationAccepted! ! case ImageActor.TimeOutImageEvaluation =>! log.info("Image Expired, received time out")! log.info("sending parent a ExpiredImageEvaluation message")! directoryActorRef ! CommonMsg.ExpiredImageEvaluation(id)! }! } Nachrichten empfangen Nachrichten senden
  7. Akteur Hierarchien Master Direct- ory Server Server Server Image Image

    Image Client Cont- roller Cont- roller REST REST RequestImage forward RequestImage via Router RequestImage create ImageActor Response reply ImageClient ImageMaster ImageServer erzeugt Timer
  8. Tell-Pattern • Empfänger ! SomeMessage! • „fire & forget“ Sender

    Emp- fänger // ImageServer def die(msg: String) = Action { directoryActor ! msg Ok }
  9. Ask-Pattern Sender Emp- fänger • Empfänger ? SomeMessage! • erwarte

    direkte Antwort vom Empfänger // ImageMaster def image = Action.async { val responseFuture = (masterActor ? RequestImageId).mapTo[Response] responseFuture.map(response => response.status match { case 200 => Ok(response.body) case _ => BadRequest(response.body) }).recover { case connEx: Exception => (ServiceUnavailable(connEx.getMessage)) } }
  10. Forward Sender Inter- mediary Emp- fänger • sender forward SomeMessage!

    • der ursprüngliche Sender wird „weitergereicht“
  11. Round Robin Router ! ! ! • router ! SomeMessage!

    • sendet an einen ausgewählten Routee! • Round Robin Router beinhaltet die Auswahllogik Router Routee Routee Routee Sender
  12. Codebeispiel class MasterActor(serverNames: List[String]) extends Actor with ActorLogging with Configured

    { ! … val roundRobinRouter = context.actorOf(Props.empty.withRouter( RoundRobinRouter(routees = serverRoutees)), "RoundRobinRouter") ! def receive = LoggingReceive { case RequestImageId => roundRobinRouter forward RequestImageId case … => … } ! } Konfiguration Verwenden Bekommt der MasterActor eine Anfrage nach einer ImageId, ! sendet er die Anfrage an den RoundRobinRouter weiter.
  13. BroadCast Router ! • router ! SomeMessage! • sendet einen

    Broadcast an alle Routees Router Routee Routee Routee Sender
  14. Codebeispiel class MasterActor(serverNames: List[String]) extends Actor with ActorLogging with Configured

    { ! … ! val broadCastRouter = context.actorOf(Props.empty.withRouter( BroadcastRouter(routees = serverRoutees)), "BroadCastRouter") ! def receive = LoggingReceive { … ! case Ping => broadCastRouter forward Ping } ! } Konfiguration Verwenden Der MasterActor fragt die einzelnen Server periodisch ab! und leitet Ping Anfragen an alle Server weiter.
  15. ScatterGather Router • router ! SomeMessage! • sendet an alle

    Routees, und leitet die erste Antwort zurück. Router Routee Routee Routee Sender
  16. Codebeispiel class MasterActor(serverNames: List[String]) extends Actor with ActorLogging with Configured

    { ! … val scatterGatherRouter = context.actorOf(Props.empty.withRouter( ScatterGatherFirstCompletedRouter(routees = serverRoutees, within = appConfig.defaultTimeout)), "ScatterGatherRouter") ! case e: Evaluation => log.info(""" forwarding evaluation to scatterGatherRouter """) scatterGatherRouter forward e ! } ! } Konfiguration Verwenden Erhält der MasterActor eine Evaluation (d.h. eine Liste vonTags für ein Bild),! leitet er die Anfrage weiter ! und bekommt eine Antwort vom ersten Server, ! der sich dafür zuständig erklärt.
  17. Circuit Breaker => Failure • Circuit Breaker haben einen definierten

    Timeout und einen Retry Counter! • Alles gut? => Closed state! • Timeout und Counter erreicht => Open State! • Open State und Request => probiere einen Request alle andere werden abgelehnt! • Half Open und Request => Alles Gut? => Closed sonst Open Closed Open Halfopen
  18. Codebeispiel /** * Represents a server serving images. * */

    class ServerActor(url: String) extends Actor with ActorLogging { … val breaker = new CircuitBreaker(context.system.scheduler, maxFailures = 3, callTimeout = 1 seconds, resetTimeout = 30 seconds) def receive = LoggingReceive { case RequestImageId => breaker.withCircuitBreaker(WS.url(imageUrl).get) pipeTo sender ! case … } } Konfigurieren Verwenden Der Master verlangt vom ImageServer eine neue ImageId.! Schlägt der Aufruf 3x fehl (maxFailures), ! werden weitere Aufrufe sofort mit Fehler zurückgemeldet, ! bis sich der CirucitBreaker wieder schliesst.
  19. Akteur Supervisor => Failure Master Server Server Server Client Cont-

    roller REST RequestImage forward RequestImage via Router reply ! Jeder Akteur hat einen zugewiesenen Supervisor.! • OneForOneStrategy: Supervisor behandelt den Fehler für den fehlgeschlagenen Akteur! • AllForOneStrategy: Supervisor behandelt den Fehler für alle „Siblings“ Exception
  20. Codebeispiel // DirectoryActor receives Notification if it „died“ and the

    Supervisor handles the exception according // to the plan … override val supervisorStrategy = OneForOneStrategy(maxNrOfRetries = 10, withinTimeRange = 1 minute) { case _: UnsupportedOperationException 㱺 Resume case _: FileNotFoundException 㱺 Restart case _: ActorKilledException 㱺 Stop case _: Exception 㱺 Escalate } … Konfiguration Exceptions
  21. Apache Camel Framework zur Umsetzung von Enterprise Integration Patterns! /**

    * Camel Producer endpoint which is configured via [[util.AppConfig.camelEndpoint]]. */ class CamelActor extends Actor with Producer with Oneway with Configured with ActorLogging{ ! lazy val appConfig = configured[AppConfig] def endpointUri = appConfig.camelEndpoint // e.g. "activemq:evaluations" or „file://tmp/camel/…“ ! } … // send Json as Inputstream to CamelActor val is = IOUtils.toInputStream(Json.prettyPrint(Json.toJson(eval))) val headerMap = Map(Exchange.FILE_NAME -> extractFileName(id)) camelActor ! CamelMessage(body = is, headers = headerMap)