Reaktive Applikationen mit Scala, Play und Akka

28
Markus Klink, @joheinz, [email protected] Reactive Applications Scala, Play2 und Akka

description

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.

Transcript of Reaktive Applikationen mit Scala, Play und Akka

Page 1: Reaktive Applikationen mit Scala, Play und Akka

Markus Klink, @joheinz, [email protected]

Reactive Applications !

Scala, Play2 !und Akka

Page 2: Reaktive Applikationen mit Scala, Play und Akka

All in one

Page 3: Reaktive Applikationen mit Scala, Play und Akka

MicroservicesKommunikation für REST Schnittstellen!

viele kleine Systeme!

Technologiemix

Page 4: Reaktive Applikationen mit Scala, Play und Akka

Reactive Applications

• ereignisgetrieben!

• skalierbar!

• widerstandsfähig!

• reaktionsschnell

• HTML5/Javascript / Akteure!

• zustandslos!

• fehlerresistent / ausfallsicher

http://reactivemanifesto.org

Page 5: Reaktive Applikationen mit Scala, Play und Akka

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

Page 6: Reaktive Applikationen mit Scala, Play und Akka

Play Framework

Asynchron

JSON

Zustandslos

HTTP

Fast Redeployment

Scala & Java

Websockets

Page 7: Reaktive Applikationen mit Scala, Play und Akka

Fallbeispiel: „Simple Mechanical Turk“

Client

Client

Client

Master

Server

Server

Server

Page 8: Reaktive Applikationen mit Scala, Play und Akka

Play

• Asynchron, non blocking!

• keine Abstraktion über HTTP Standard!

• JSON friendly!

• zustandslos!

• Akka built in!

• Websockets, Eventsource, Comet, …

Page 9: Reaktive Applikationen mit Scala, Play und Akka

Fallbeispiel

!

• 3 Rollen: Client, Master, Server!

• interne Kommunikation über Akteure!

• externe Kommunikation über Akteure und Rest/Webservices

Page 10: Reaktive Applikationen mit Scala, Play und Akka

Was ist ein Akteur?Akteure kapseln Zustand, Verhalten, besitzen eine Mailbox und bilden Hierarchien.!!

Akteure kommunizieren über Nachrichten.!!

Page 11: Reaktive Applikationen mit Scala, Play und Akka

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

Page 12: Reaktive Applikationen mit Scala, Play und Akka

Akteur Hierarchien

Master Direct-ory

Server Server Server Image ImageImage

Client

Cont-roller

Cont-roller

REST

REST

RequestImage

forward RequestImage via Router

RequestImage

create ImageActor

Response

reply

ImageClient ImageMaster ImageServer

erzeugt Timer

Page 13: Reaktive Applikationen mit Scala, Play und Akka

Tell-Pattern

• Empfänger ! SomeMessage!

• „fire & forget“

Sender Emp-fänger

// ImageServerdef die(msg: String) = Action { directoryActor ! msg Ok }

Page 14: Reaktive Applikationen mit Scala, Play und Akka

Ask-Pattern

Sender Emp-fänger

• Empfänger ? SomeMessage!

• erwarte direkte Antwort vom Empfänger

// ImageMasterdef 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)) }}

Page 15: Reaktive Applikationen mit Scala, Play und Akka

Forward

Sender

Inter-mediary Emp-

fänger

• sender forward SomeMessage!

• der ursprüngliche Sender wird „weitergereicht“

Page 16: Reaktive Applikationen mit Scala, Play und Akka

Router

Page 17: Reaktive Applikationen mit Scala, Play und Akka

Round Robin Router

!

!

!

• router ! SomeMessage!

• sendet an einen ausgewählten Routee!

• Round Robin Router beinhaltet die Auswahllogik

Router

Routee

Routee

Routee

Sender

Page 18: Reaktive Applikationen mit Scala, Play und Akka

Codebeispielclass 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.

Page 19: Reaktive Applikationen mit Scala, Play und Akka

BroadCast Router

!

• router ! SomeMessage!

• sendet einen Broadcast an alle Routees

Router

Routee

Routee

Routee

Sender

Page 20: Reaktive Applikationen mit Scala, Play und Akka

Codebeispielclass 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.

Page 21: Reaktive Applikationen mit Scala, Play und Akka

ScatterGather Router

• router ! SomeMessage!

• sendet an alle Routees, und leitet die erste Antwort zurück.

Router

Routee

Routee

Routee

Sender

Page 22: Reaktive Applikationen mit Scala, Play und Akka

Codebeispielclass 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.

Page 23: Reaktive Applikationen mit Scala, Play und Akka

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

Page 24: Reaktive Applikationen mit Scala, Play und Akka

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.

Page 25: Reaktive Applikationen mit Scala, Play und Akka

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

Page 26: Reaktive Applikationen mit Scala, Play und Akka

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

Page 27: Reaktive Applikationen mit Scala, Play und Akka

Apache CamelFramework 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 CamelActorval is = IOUtils.toInputStream(Json.prettyPrint(Json.toJson(eval)))val headerMap = Map(Exchange.FILE_NAME -> extractFileName(id))camelActor ! CamelMessage(body = is, headers = headerMap)

Page 28: Reaktive Applikationen mit Scala, Play und Akka

Code