Upload
markus-klink
View
2.139
Download
1
Embed Size (px)
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.
Citation preview
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