Reaktive Applikationen mit Scala, Play und Akka
-
Upload
markus-klink -
Category
Documents
-
view
2.130 -
download
1
description
Transcript of Reaktive Applikationen mit Scala, Play und Akka
All in one
MicroservicesKommunikation für REST Schnittstellen!
viele kleine Systeme!
Technologiemix
Reactive Applications
• ereignisgetrieben!
• skalierbar!
• widerstandsfähig!
• reaktionsschnell
• HTML5/Javascript / Akteure!
• zustandslos!
• fehlerresistent / ausfallsicher
http://reactivemanifesto.org
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
Play Framework
Asynchron
JSON
Zustandslos
HTTP
Fast Redeployment
Scala & Java
Websockets
Fallbeispiel: „Simple Mechanical Turk“
Client
Client
Client
Master
Server
Server
Server
Play
• Asynchron, non blocking!
• keine Abstraktion über HTTP Standard!
• JSON friendly!
• zustandslos!
• Akka built in!
• Websockets, Eventsource, Comet, …
Fallbeispiel
!
• 3 Rollen: Client, Master, Server!
• interne Kommunikation über Akteure!
• externe Kommunikation über Akteure und Rest/Webservices
Was ist ein Akteur?Akteure kapseln Zustand, Verhalten, besitzen eine Mailbox und bilden Hierarchien.!!
Akteure kommunizieren über Nachrichten.!!
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
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
Tell-Pattern
• Empfänger ! SomeMessage!
• „fire & forget“
Sender Emp-fänger
// ImageServerdef die(msg: String) = Action { directoryActor ! msg Ok }
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)) }}
Forward
Sender
Inter-mediary Emp-
fänger
• sender forward SomeMessage!
• der ursprüngliche Sender wird „weitergereicht“
Router
Round Robin Router
!
!
!
• router ! SomeMessage!
• sendet an einen ausgewählten Routee!
• Round Robin Router beinhaltet die Auswahllogik
Router
Routee
Routee
Routee
Sender
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.
BroadCast Router
!
• router ! SomeMessage!
• sendet einen Broadcast an alle Routees
Router
Routee
Routee
Routee
Sender
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.
ScatterGather Router
• router ! SomeMessage!
• sendet an alle Routees, und leitet die erste Antwort zurück.
Router
Routee
Routee
Routee
Sender
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.
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
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.
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
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
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)
Code