Download - Reaktive Applikationen mit Scala, Play und Akka

Transcript
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