patterns de développement pour une application web réactive et concurrente
DESCRIPTION
Nous avons remporté le deuxième prix du concours Typesafe grâce à l’application Carracedashboard dont l’objectif était de démontrer les possibilités du stack Typesafe. Carracedashboard est une simulation et un suivi en temps réel d’une course automobile. Nous utiliserons cet exemple pour illustrer plusieurs cas d’utilisation d’acteurs Akka pour le développement d’une application web et en quoi la programmation fonctionnelle et l’API Iteratee de Play! rend intuitif le développement d’applications de simulation. Nous montrerons également comment utiliser HTML5 pour améliorer la fluidité des déplacements sur les cartes.TRANSCRIPT
Patterns de développement pour une application Web réactive et concurrente
25/10/2013
Qui sommes-nous ? @fXzo @antoined
De quoi allons nous vous parler ? • Un concours : Typesafe Developer Contest • Une application : Car Race Dashboard • Quelques cas d’utilisation des Actors Akka
et des Iteratees
Des patterns pour... • Effectuer des traitements asynchrones • Gérer des états dans notre application • Travailler avec un stream d’événements
L’application
Nb file Nb lines Avg lines scala 9 770 86 coffee 7 343 49 html 4 275 68
Environ 20 heures de travail sur une semaine
Le résultat « Fabrice and Antoine have designed an app that is simply awesome, and a very, very close runner up to the winner. It’s far more than just a sample… and the judge were truly wowed»
https://github.com/intechgrp/CarRaceDashboard
Démo
Architecture globale
Simulation
Race
Car Car Car Car Car
Track = List [TrackPoint]
Navigateur
Moteur
Flux SSE
Navigateur
RTevListener RTevListener
Akka.system.eventStream
Stream.events: Enumerator Storage MongoDB
Stats
Architecture globale
Simulation
Race
Car Car Car Car Car
Track = List [TrackPoint]
Navigateur
Moteur
Flux SSE
Navigateur
RTevListener RTevListener
Akka.system.eventStream
Stream.events: Enumerator Storage MongoDB
Stats
La course
case class Position(latitude: Double, longitude: Double)
case class TrackPoint(id: Int, position: Position)
type Track = List[TrackPoint]
Déplacement des voitures
//return new CheckPoint on the track at distance d from point
private def next(point: TrackPoint, d: Double): TrackPoint
La Simulation - CarActor • Un Acteur Akka par coureur
o Gère l’état courant du coureur : sa position actuelle, sa vitesse instantanée, la distance parcourue
o Réagit à différents messages : § “move” : se déplace à la position suivante § “start” : début de la course, c.a.d. schedule
l’envoi de messages “move” § “getState” : retourne l’état courant
La Simulation - RaceActor • Un Acteur Akka pour gérer tous les coureurs
o Envoie le message “start” à tous les coureurs au démarrage de la course : utilisation d’un BroadcastRouter
router = context.actorOf(Props[CarActor].withRouter(akka.routing.BroadcastRouter(currentRace.get.carActors)))
/ Fire start event : broadcast the event to all CarActors
router ! "start"
Architecture globale
Simulation
Race
Car Car Car Car Car
Track = List [TrackPoint]
Navigateur
Moteur
Flux SSE
Navigateur
RTevListener RTevListener
Akka.system.eventStream
Stream.events: Enumerator Storage MongoDB
Stats
La Simulation - RaceActor o Produit le flux d’événements à partir des CarActor et
le lie au Moteur
→ pour chaque événement produit par Streams, transmet l’événement au Moteur (“StorageActor”)
// Connect the event stream to the storage actor
new Streams(currentRace.get).events(Iteratee.foreach[models.Events.Event] {
event => models.StorageActor.actor ! event
})
Architecture globale
Simulation
Race
Car Car Car Car Car
Track = List [TrackPoint]
Navigateur
Moteur
Flux SSE
Navigateur
RTevListener RTevListener
Akka.system.eventStream
Stream.events: Enumerator Storage MongoDB
Stats
• Le moteur est un acteur Akka recevant tous les événements émis par tous les véhicules
• Pour chaque événement reçu, l’acteur : o Publie l’événement sur le stream d’événements
Akka o Stocke l’événement dans une collection MongoDB
Le moteur
Le moteur • Un autre acteur (StatsActor) est planifié pour calculer
régulièrement des statistiques (vitesse moyenne, min, max, …) par voiture
• Cet acteur se base sur les données insérées dans MongoDB
• Pour chaque statistique calculée, l’actor publie un message dans l’eventStream Akka
Architecture globale
Simulation
Race
Car Car Car Car Car
Track = List [TrackPoint]
Navigateur
Moteur
Flux SSE
Navigateur
RTevListener RTevListener
Akka.system.eventStream
Stream.events: Enumerator Storage MongoDB
Stats
Flux SSE • Pour chaque navigateur accédant à l’application, un
acteur « RTEventListener » est créé • Cet acteur est à l’écoute de l’eventStream Akka • Pour chaque message sur ce stream, l’acteur envoie
dans le flux Server Sent Event une représentation JSON du message
Interface • Au niveau de l’interface HTML5, le flux SSE est
connecté avec l’objet JavaScript « EventSource » • Pour chaque message reçu sur ce flux, l’interface est
mise à jour en fonction du type d’événement : • Nouvelle position : déplace la voiture concernée • Statistiques : met à jour le récapitulatif • Vitesse instantée / distance parcourue : met à jour le compteur
Merci !
Questions ?