Reactive streams
Étape 1
Vocabulaire
Programmation réactive
- Disponibles : le système doit répondre rapidement quoi qu'il arrive.
- Résilient : le système doit toujours rester disponible, même en cas d'erreur.
- Souples : le système doit continuer à vivre même s'il est surchargé.
- Orienté messages : le système utilise des messages asynchrones.
— Wikipedia – https://fr.wikipedia.org/wiki/Programmation_r%C3%A9active
Stream
- Séquence de données finie ou non
- Les données sont accessibles immédiatement ou réparties dans le temps
- Différent des batchs :
- Les données du batch sont traitées comme un ensemble
- Les données du stream sont traitées une à une
Reactive Stream
- reactive stream = programmation reactive + stream
Étape 2
Les flows
Décomposition
- Division d'un algorithme complexe
- Chaque partie ne traite que d'un élément à la fois
- Chaque partie peut être sur un système différent
Traitements
- Filtres
- Transformation
Schéma
Source | [-> |
Flow | -> |
Sink | ->] |
Exemple
final case class Author(handle: String)
final case class Hashtag(name: String)
final case class Tweet(author: Author, timestamp: Long, body: String) {
def hashtags: Set[Hashtag] =
body.split(" ").collect { case t if t.startsWith("#") => Hashtag(t) }.toSet
}
object Boot extends App {
implicit val system = ActorSystem("Exemple")
implicit val materializer = ActorMaterializer()
val akka = Hashtag("#akka")
val tweets: Source[Tweet, Unit] = ???
val authors: Source[Author, NotUsed] =
tweets // Source
.filter(_.hashtags.contains(akka)) // Flow
.map(_.author) // Flow
.runWith(Sink.foreach(println)) // Sink
}
http://doc.akka.io/docs/akka/2.4.2/scala/stream/stream-quickstart.html#reactive-tweets
Remarque
Même API que les collections !
Étape 3
Les graphs
Comme les flows…
- Décomposition
- Traitements
… mais avec plusieurs entrées et sorties
Récupération des coordonnées GPS à partir de 2 API externes
… mais avec plusieurs entrées et sorties
Récupération des coordonnées GPS à partir de 2 API externes
package graph
import java.util.Date
import akka.actor.ActorSystem
import akka.stream._
import akka.stream.scaladsl._
import scala.concurrent.Future
trait Graph {
case class Address(address: String)
case class Location(x: Int, y: Int)
def addressToLocationApi1(address: Address): Future[Location] = ???
def addressToLocationApi2(address: Address): Future[Location] = ???
import GraphDSL.Implicits._
// Get location in terms of address based on two external API.
val addressToLocation = GraphDSL.create() { implicit builder =>
val A = builder.add(Broadcast[Address](2))
val B = builder.add(Flow[Address].mapAsync(1)(addressToLocationApi1(_)))
val C = builder.add(Flow[Address].mapAsync(1)(addressToLocationApi2(_)))
val D = builder.add(Merge[Location](2))
A ~> B ~> D
A ~> C ~> D
FlowShape(A.in, D.out)
}.named("addressToLocation")
}
object BootGraph extends App with Graph {
implicit val system = ActorSystem("Exemple")
implicit val materializer = ActorMaterializer()
val flow = Flow.fromGraph(addressToLocation)
val source = Source.single[Address](Address("My address"))
val sink = Sink.foreach[Location](loc => println(s"${loc.x};${loc.y}"))
}
Étape 4
Reactive ?
Le problème
Que faire si un consommateur est plus long que son producteur ?
Le problème
Que faire si un consommateur est plus long que son producteur ?
Ralentir le flux
L'objectif sera de ralentir le flux pour répartir correctement la charge
Conclusion
- Les reactive streams sont gérées par le framework
- Ne change pas la logique métier
- Traitement de collections
- Un élément à la fois
- Concentration sur la répartition physique des charges
Reactive stream
By Gaëtan Rizio
Reactive stream
Que sont les reactive streams et les graphs ?
- 435