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