Pro Yearly is on sale from $80 to $50! »

RxJava, RxJava 2, Reactor : Etat de l’art des Reactive Streams en Java

7843bb075c05be6886a97b77e36758ff?s=47 David
April 06, 2017

RxJava, RxJava 2, Reactor : Etat de l’art des Reactive Streams en Java

2017 sera l’année des Reactive Streams en Java : RxJava est largement utilisé sur Android ou côté backend à travers Hystrix. RxJava 2 vient tout juste de sortir et va pouvoir profiter du chemin déjà tracé par sa précédente version. Pivotal pousse Reactor qui sera disponible dans le framework Spring version 5.

Qu’apportes ces différentes implémentations, en terme d’API ou de performances ? En quoi sont-elles différentes ? Faut-il migrer sur RxJava 2 tout de suite ? Reactor va-t’il remplacer RxJava et se limite t'il au framework Spring ?

Cette conférence fera un bref historique des ces bibliothèques avant de faire une comparaison des APIs, performances et cas d’utilisation de ces Reactive Streams.

Devoxx France ( Paris ) - 6 Avril 2017

7843bb075c05be6886a97b77e36758ff?s=128

David

April 06, 2017
Tweet

Transcript

  1. RxJava, RxJava 2, Reactor État de l’art des Reactive Streams

    sur la JVM
  2. David Wursteisen

  3. None
  4. Écrire du code asynchrone : 
 ça craint

  5. Future ExecutorService ex = Executors.newCachedThreadPool();
 Future<String> future = ex.submit(() ->

    longProcessing());
 String result = future.get(); Appel bloquant
  6. Future Future<?> future1 = /* ... */
 Future<?> future2 =

    /* ... */
 Future<?> future3 = /* ... */
 Future<?> future4 = /* ... */
 Future<?> future5 = /* ... */ Orchestration optimale ?
  7. Callback RemoteService service = buildRemoteService();
 service.getUser(id -> {
 service.getData(id, data

    -> {
 service.getSomething(data, whut -> { service.neverEndingCallBack(whut, () -> { }); });
 });
 }); Callback Hell
  8. Relationship Status:
 it’s complicated

  9. Le problème du code synchrone

  10. Synchrone Attente de la réponse Appelant Appelé

  11. Impacte le temps de réponse Serveur lent

  12. Il y a toujours un truc qui ne marche pas

    ça m’énerve !
  13. None
  14. Ça ne marche pas ! ça a planté ? dans

    le doute, reboot
  15. Asynchrone Appelant Appelé Traitement continu

  16. L’asynchrone permet de profiter du temps d’attente

  17. Impact minimum sur le temps de réponse

  18. None
  19. Écrire du code asynchrone facilement ?

  20. Émergence 
 de différentes approches

  21. 2 Reactive Streams

  22. Reactive Streams API Reactive Streams RxJava 2 Reactor Interface Implémentation

  23. L’API Reactive Streams est 
 un pont 
 entre les

    implémentations
  24. Contrat Reactive Streams onSubscribe onNext onError onComplete

  25. RxJava n’est pas compatible 
 avec Reactive Streams (il faut

    alors utiliser un adapteur : RxJavaReactiveStreams) https://github.com/ReactiveX/RxJavaReactiveStreams
  26. onNext * ( onError | onComplete ) 
 onNext *

    ( onError | onCompleted ) RxJava Reactive Streams
  27. onNext * ( onError | onComplete ) 
 onNext *

    ( onError | onCompleted ) RxJava Reactive Streams Nom différent
  28. Reactor peut utiliser RxJava 2 et réciproquement

  29. Approche commune

  30. API pour manipuler 
 des événements de manière synchrone ou

    asynchrone à travers un flux d’événements
  31. None
  32. Map ( ⃝ →  ⬛)

  33. None
  34. None
  35. remoteApi.people(1).flatMap(luke -> { 
 Observable<String> vehicles = Observable.from(luke.getVehiclesIds())
 .flatMap(remoteApi::vehicle)
 .map(vehicle

    -> luke.getName() + " can drive " + vehicle.getName());
 
 Observable<String> starships = Observable.from(luke.getStarshipsIds())
 .flatMap(remoteApi::starship)
 .map(starship -> luke.getName() + " can fly with " + starship.getName());
 
 return Observable.merge(vehicles, starships); 
 }).subscribe(System.out::println);
  36. remoteApi.people(1).flatMap(luke -> { 
 Observable<String> vehicles = Observable.from(luke.getVehiclesIds())
 .flatMap(remoteApi::vehicle)
 .map(vehicle

    -> luke.getName() + " can drive " + vehicle.getName());
 
 Observable<String> starships = Observable.from(luke.getStarshipsIds())
 .flatMap(remoteApi::starship)
 .map(starship -> luke.getName() + " can fly with " + starship.getName());
 
 return Observable.merge(vehicles, starships); 
 }).subscribe(System.out::println);
  37. remoteApi.people(1).flatMap(luke -> { 
 Observable<String> vehicles = Observable.from(luke.getVehiclesIds())
 .flatMap(remoteApi::vehicle)
 .map(vehicle

    -> luke.getName() + " can drive " + vehicle.getName());
 
 Observable<String> starships = Observable.from(luke.getStarshipsIds())
 .flatMap(remoteApi::starship)
 .map(starship -> luke.getName() + " can fly with " + starship.getName());
 
 return Observable.merge(vehicles, starships); 
 }).subscribe(System.out::println); Push du résultat
  38. remoteApi.people(1).flatMap(luke -> { 
 Observable<String> vehicles = Observable.from(luke.getVehiclesIds())
 .flatMap(remoteApi::vehicle)
 .map(vehicle

    -> luke.getName() + " can drive " + vehicle.getName());
 
 Observable<String> starships = Observable.from(luke.getStarshipsIds())
 .flatMap(remoteApi::starship)
 .map(starship -> luke.getName() + " can fly with " + starship.getName());
 
 return Observable.merge(vehicles, starships); 
 }).subscribe(System.out::println); Récupération des véhicules de Luke Récupération des vaisseaux de Luke
  39. remoteApi.people(1).flatMap(luke -> { 
 Observable<String> vehicles = Observable.from(luke.getVehiclesIds())
 .flatMap(remoteApi::vehicle)
 .map(vehicle

    -> luke.getName() + " can drive " + vehicle.getName());
 
 Observable<String> starships = Observable.from(luke.getStarshipsIds())
 .flatMap(remoteApi::starship)
 .map(starship -> luke.getName() + " can fly with " + starship.getName());
 
 return Observable.merge(vehicles, starships); 
 }).subscribe(System.out::println); Flux 
 d’événements Flux 
 d’événements
  40. remoteApi.people(1).flatMap(luke -> { 
 Observable<String> vehicles = Observable.from(luke.getVehiclesIds())
 .flatMap(remoteApi::vehicle)
 .map(vehicle

    -> luke.getName() + " can drive " + vehicle.getName());
 
 Observable<String> starships = Observable.from(luke.getStarshipsIds())
 .flatMap(remoteApi::starship)
 .map(starship -> luke.getName() + " can fly with " + starship.getName());
 
 return Observable.merge(vehicles, starships); 
 }).subscribe(System.out::println); Fusion des 2 flux
  41. remoteApi.people(1).flatMap(luke -> { 
 Observable<String> vehicles = Observable.from(luke.getVehiclesIds())
 .flatMap(remoteApi::vehicle)
 .map(vehicle

    -> luke.getName() + " can drive " + vehicle.getName());
 
 Observable<String> starships = Observable.from(luke.getStarshipsIds())
 .flatMap(remoteApi::starship)
 .map(starship -> luke.getName() + " can fly with " + starship.getName());
 
 return Observable.merge(vehicles, starships); 
 }).subscribe(System.out::println); Déclenche l’exécution du code
  42. remoteApi.people(1).flatMap(luke -> { 
 Observable<String> vehicles = Observable.from(luke.getVehiclesIds())
 .flatMap(remoteApi::vehicle)
 .map(vehicle

    -> luke.getName() + " can drive " + vehicle.getName());
 
 Observable<String> starships = Observable.from(luke.getStarshipsIds())
 .flatMap(remoteApi::starship)
 .map(starship -> luke.getName() + " can fly with " + starship.getName());
 
 return Observable.merge(vehicles, starships); 
 }).subscribe(System.out::println); Flux 
 d’événements
  43. None
  44. Button btn = new Button();
 btn.setText("Click Me");
 
 JavaFx.fromClick(btn) 


    .observeOn(Schedulers.io()) 
 .switchMap(evt -> remoteApi.getData())
 .observeOn(javaFx()) 
 .doOnNext(value -> btn.setText("Data: " + value))
 .subscribe();
  45. Button btn = new Button();
 btn.setText("Click Me");
 
 JavaFx.fromClick(btn) //

    Observable<Event>
 .observeOn(Schedulers.io())
 .switchMap(evt -> remoteApi.getData())
 .observeOn(javaFx()) 
 .doOnNext(value -> btn.setText("Data: " + value))
 .subscribe(); Écoute des clicks
  46. Button btn = new Button();
 btn.setText("Click Me");
 
 JavaFx.fromClick(btn) //

    Observable<Event>
 .observeOn(Schedulers.io())
 .switchMap(evt -> remoteApi.getData())
 .observeOn(javaFx()) 
 .doOnNext(value -> btn.setText("Data: " + value))
 .subscribe(); Changement du contexte d’exécution
  47. Button btn = new Button();
 btn.setText("Click Me");
 
 JavaFx.fromClick(btn) //

    Observable<Event>
 .observeOn(Schedulers.io())
 .switchMap(evt -> remoteApi.getData()) // Observable<Data>
 .observeOn(javaFx()) 
 .doOnNext(value -> btn.setText("Data: " + value))
 .subscribe(); Appel asynchrone d’un web service
  48. Button btn = new Button();
 btn.setText("Click Me");
 
 JavaFx.fromClick(btn) //

    Observable<Event>
 .observeOn(Schedulers.io())
 .switchMap(evt -> remoteApi.getData()) // Observable<Data>
 .observeOn(javaFx()) 
 .doOnNext(value -> btn.setText("Data: " + value))
 .subscribe(); Changement du contexte d’exécution
  49. Button btn = new Button();
 btn.setText("Click Me");
 
 JavaFx.fromClick(btn) //

    Observable<Event>
 .observeOn(Schedulers.io())
 .switchMap(evt -> remoteApi.getData()) // Observable<Data>
 .observeOn(javaFx()) 
 .doOnNext(value -> btn.setText("Data: " + value))
 .subscribe(); Changement au niveau de l’UI
  50. Button btn = new Button();
 btn.setText("Click Me");
 
 JavaFx.fromClick(btn) //

    Observable<Event>
 .observeOn(Schedulers.io())
 .switchMap(evt -> remoteApi.getData()) // Observable<Data>
 .observeOn(javaFx()) 
 .doOnNext(value -> btn.setText("Data: " + value))
 .subscribe(); Flux 
 d’événements
  51. Button btn = new Button();
 btn.setText("Click Me");
 
 ExecutorService io

    = Executors.newSingleThreadExecutor();
 btn.setOnAction(evt -> {
 io.submit(() -> {
 remoteApi.getData(data -> {
 Platform.runLater(() -> {
 btn.setText(data);
 });
 });
 });
 }); Sac de noeud Structure pyramidale
  52. Grâce à RxJava et Reactor …

  53. Écrire du code asynchrone : 
 ça craint

  54. Il était une fois…

  55. Création des 
 Reactive Extensions Création de RxJava Participation active

    Reprise de 
 RxJava & RxJava 2 Création de Reactor Participation à Reactor
  56. RxJava 
 est une technologie 
 éprouvée

  57. Reactor
 profite de l’expérience de 
 RxJava (et vice versa)

  58. Types d’objet

  59. Observable

  60. None
  61. None
  62. None
  63. None
  64. None
  65. Single

  66. None
  67. Completable

  68. None
  69. RxJava Contrat Backpressure Observable [N] Oui Single [1] Non Completable

    [0] Non Ajouté après coup Appel Web service Processus d’arrière plan
  70. Écouter une websocket, pour c h a q u e

    c o m m a n d e r e ç u e , composer une réponse à partir de 3 webservices différents, puis exécuter 2 jobs séquentiellement ?
  71. Écouter une websocket, pour c h a q u e

    c o m m a n d e r e ç u e , composer une réponse à partir de 3 webservices différents, puis exécuter 2 jobs séquentiellement ?
  72. Écouter une websocket, pour c h a q u e

    c o m m a n d e r e ç u e , composer une réponse à partir de 3 webservices différents, puis exécuter 2 jobs séquentiellement ?
  73. Écouter une websocket, pour c h a q u e

    c o m m a n d e r e ç u e , composer une réponse à partir de 3 webservices différents, puis exécuter 2 jobs séquentiellement ?
  74. websocket("/topics/cmd")
 .observeOn(Schedulers.io())
 .switchMap(cmd ->
 Single.zip(
 api.getActions(),
 api.getScore(),
 api.getUserData(),
 this::composeResult).toObservable())
 .observeOn(Schedulers.computation())


    .concatMap(result -> updateDb(result).andThen(getLastResults()))
 .subscribe(last -> System.out.println("last results -> " + last));
  75. websocket("/topics/cmd")
 .observeOn(Schedulers.io())
 .switchMap(cmd ->
 Single.zip(
 api.getActions(),
 api.getScore(),
 api.getUserData(),
 this::composeResult).toObservable())
 .observeOn(Schedulers.computation())


    .concatMap(result -> updateDb(result).andThen(getLastResults()))
 .subscribe(last -> System.out.println("last results -> " + last)); Écoute de la websocket Observable
  76. websocket("/topics/cmd")
 .observeOn(Schedulers.io())
 .switchMap(cmd ->
 Single.zip(
 api.getActions(),
 api.getScore(),
 api.getUserData(),
 this::composeResult).toObservable())
 .observeOn(Schedulers.computation())


    .concatMap(result -> updateDb(result).andThen(getLastResults()))
 .subscribe(last -> System.out.println("last results -> " + last)); Composition d’une réponse à partir de 3 webservices Single
  77. websocket("/topics/cmd")
 .observeOn(Schedulers.io())
 .switchMap(cmd ->
 Single.zip(
 api.getActions(),
 api.getScore(),
 api.getUserData(),
 this::composeResult).toObservable())
 .observeOn(Schedulers.computation())


    .concatMap(result -> updateDb(result).andThen(getLastResults()))
 .subscribe(last -> System.out.println("last results -> " + last)); Completable Exécuter 2 jobs
  78. websocket("/topics/cmd")
 .observeOn(Schedulers.io())
 .switchMap(cmd ->
 Single.zip(
 api.getActions(),
 api.getScore(),
 api.getUserData(),
 this::composeResult).toObservable())
 .observeOn(Schedulers.computation())


    .concatMap(result -> updateDb(result).andThen(getLastResults()))
 .subscribe(last -> System.out.println("last results -> " + last)); Écoute de la websocket Observable Composition d’une réponse à partir de 3 webservices Single Completable Exécuter 2 jobs
  79. RxJava 2 Contrat Backpressure Observable [N] Non Single [1] Non

    Completable [0] Non Maybe [0|1] Non Nouveau ! Concept « similaire » à Optional
  80. Maybe

  81. None
  82. Backpressure avec RxJava 2

  83. Consommateur

  84. Consommateur ça déborde !

  85. backpressure

  86. MissingBackpressureException ?

  87. acceptedIntent
 .filter(intent -> !intent.getBooleanExtra("UpdatePhoneMode", false))
 .concatMap(intent -> approximatedEngine.detectCurrentPlace())
 .doOnNext(score ->

    Log.info(TAG, "Scan completed with result " + score))
 .concatMap(this::detectSleepMode)
 .concatMap((score) -> isNewPlace(score.getScore().getPlace()).map(p -> score))
 .doOnNext((p) -> Log.info(TAG, "Current place found is : " + p))
 .subscribe()
  88. acceptedIntent
 .filter(intent -> !intent.getBooleanExtra("UpdatePhoneMode", false)) .onBackpressureDrop()
 .concatMap(intent -> approximatedEngine.detectCurrentPlace())
 .doOnNext(score

    -> Log.info(TAG, "Scan completed with result " + score))
 .onBackpressureDrop() .concatMap(this::detectSleepMode)
 .onBackpressureDrop()
 .concatMap((score) -> isNewPlace(score.getScore().getPlace()).map(p -> score))
 .doOnNext((p) -> Log.info(TAG, "Current place found is : " + p))
 .subscribe() Ajouté dans un moment de panique
  89. RxJava 2 Contrat Backpressure Observable [N] Non Single [1] Non

    Completable [0] Non Maybe [0|1] Non Nouveau ! Concept « similaire » à Optional
  90. RxJava 2 Contrat Backpressure Observable [N] Non Single [1] Non

    Completable [0] Non Maybe [0|1] Non Flowable [N] Oui Nouveau ! Observable avec 
 back pressure Concept « similaire » à Optional
  91. Observable → moins de 1000 éléments → Gestion d’élément d’interface

    graphique → Remplacement de Stream Java Flowable → plus de 10 000 éléments → contrôle de l’émission de données → Flux réseau avec contrôle de débit
  92. Que propose Reactor ?

  93. Flux Mono Maximum 1 élément

  94. Reactor Contrat Backpressure Flux [N] Oui Mono [0|1] Oui Identique

    à Flowable Flux avec 
 au maximum 
 1 élément
  95. Types d’objets et
 Reactive Streams

  96. Publisher Flux Flowable Reactive Streams

  97. Flux.range(1, 10)
 .flatMap(i -> Flux.just(1))
 .subscribe();

  98. Flux.range(1, 10)
 .flatMap(i -> Flux.just(1))
 .subscribe(); Publisher

  99. Flux.range(1, 10)
 .flatMap(i -> Flowable.just(1))
 .subscribe(); Publisher RxJava 2

  100. Flowable.defer(() -> Flux.range(1, 10))
 .subscribe(System.out::println); RxJava 2 Reactor

  101. Flux.defer(() -> Flowable.range(1, 10))
 .subscribe(System.out::println); RxJava 2 Reactor

  102. Encore plus de possibilités avec reactor-addons https://github.com/reactor/reactor-addons

  103. Observable.range(1, 10)
 .to(obs -> RxJava2Adapter.observableToFlux(obs, BackpressureStrategy.BUFFER))
 .checkpoint()
 .subscribe(); Maybe.empty()
 .to(maybe

    -> RxJava2Adapter.maybeToMono(maybe))
 .checkpoint()
 .subscribe(); Single.just(1)
 .to(single -> RxJava2Adapter.singleToMono(single))
 .checkpoint()
 .subscribe(); Observable → Flux Single → Mono Maybe → Mono
  104. RxJava2Adapter.monoToFlowable(Mono.just(1))
 .timestamp()
 .subscribe();
 
 RxJava2Adapter.fluxToFlowable(Flux.range(1, 10))
 .timestamp()
 .subscribe(); Mono →

    Flowable Flux → Flowable
  105. P o u v o i r u t i

    l i s e r u n e biblio t hèq ue utilisant RxJava 2 dans son projet Reactor (et vice versa)
  106. Opérateurs

  107. Catalogue conséquent et homogène

  108. all amb ambArray ambWith any as awaitOnSubscribe blockFirst blockFirstMillis blockLast

    blockLastMillis blockingFirst blockingForEach blockingIterable blockingLast blockingLatest blockingMostRecent blockingNext blockingSingle blockingSubscribe buffer bufferMillis bufferSize bufferTimeout bufferTimeoutMillis bufferUntil bufferWhile cache cacheWithInitialCapacity cancelOn cast checkpoint collect collectInto collectList collectMap collectMultimap collectSortedList combineLatest combineLatestDelayError compose concat concatArray concatArrayDelayError concatArrayEager concatDelayError concatEager concatMap concatMapDelayError concatMapEager concatMapEagerDelayError concatMapIterable concatWith contains count create debounce defaultIfEmpty defer delay delayElements delayElementsMillis delayMillis delaySubscription delaySubscriptionMillis dematerialize distinct distinctUntilChanged doAfterNext doAfterTerminate doFinally doOnCancel doOnComplete doOnEach doOnError doOnLifecycle doOnNext doOnRequest doOnSubscribe doOnTerminate elapsed elementAt elementAtOrError empty equals error filter first firstElement firstEmitting firstEmittingWith firstOrError flatMap flatMapCompletable flatMapIterable flatMapMaybe flatMapSequential flatMapSingle forEach forEachWhile from fromArray fromCallable fromFuture fromIterable fromPublisher fromStream generate getClass getPrefetch groupBy groupJoin handle hasElement hasElements hashCode hide ignoreElements interval intervalMillis intervalRange isEmpty join just last lastElement lastOrError lift limitRate log map mapError materialize merge mergeArray mergeArrayDelayError mergeDelayError mergeSequential mergeWith never next notify notifyAll observeOn ofType onBackpressureBuffer onBackpressureDrop onBackpressureError onBackpressureLatest onErrorResumeNext onErrorResumeWith onErrorReturn onErrorReturnItem onExceptionResumeNext onTerminateDetach parallel publish publishNext publishOn range rangeLong rebatchRequests reduce reduceWith repeat repeatUntil repeatWhen replay replayMillis retry retryUntil retryWhen safeSubscribe sample sampleFirst sampleFirstMillis sampleMillis sampleTimeout scan scanWith sequenceEqual serialize share single singleElement singleOrEmpty singleOrError skip skipLast skipMillis skipUntil skipUntilOther skipWhile sort sorted startWith startWithArray strict subscribe subscribeOn subscribeWith switchIfEmpty switchMap switchMapDelayError switchOnError switchOnNext switchOnNextDelayError take takeLast takeMillis takeUntil takeUntilOther takeWhile test then thenEmpty thenMany throttleFirst throttleLast throttleWithTimeout timeInterval timeout timeoutMillis timer timestamp to toFuture toIterable toList toMap toMultimap toObservable toSortedList toStream toString transform unsafeCreate unsubscribeOn using wait window windowMillis windowTimeout windowTimeoutMillis windowUntil windowWhile withLatestFrom zip zipArray zipIterable zipWith zipWithIterable
  109. RxJava RxJava 2 Reactor flatMap flatMap flatMap Émission d’aucune, une

    ou plusieurs valeurs amb amb firstEmitting Émission des valeurs du flux répondant en premier … … … … debounce debounce N/A Ignore les événements pendant un laps de temps
  110. RxJava RxJava 2 Reactor flatMap flatMap flatMap Émission d’aucune, une

    ou plusieurs valeurs amb amb firstEmitting Émission des valeurs du flux répondant en premier … … … … debounce debounce N/A Ignore les événements pendant un laps de temps Renommage
  111. L’ensemble des opérateurs couvre de nombreuses situations

  112. Nota bene

  113. https://github.com/ReactiveX/RxJava/wiki/Implementing-custom-operators-(draft) writing operators is hard when one writes an operator,

    the Observable protocol, unsubscription, backpressure and concurrency have to be taken into account and adhered to the letter
  114. Écrire un opérateur avec RxJava 2 est plus complexe qu’avec

    RxJava
  115. Rendre son application Reactive

  116. -Jake Wharton « Unless you can model your entire system

    synchronously, a single asynchronous source breaks imperative programming »
  117. -Jake Wharton « Unless you can model your entire system

    synchronously, a single asynchronous source breaks imperative programming »
  118. Reactive Reactive API Synchrone Callback Reactive Reactive

  119. Utilisation de Factory

  120. RxJava
 RxJava 2 Reactor Flowable.just Flux.just Émission d’une valeur existante

    Flowable.defer Flux.defer Émission tardive d’un Observable Flowable.fromCallable Mono.fromCallable Émission à partir d’un appel de fonction Flowable.create Flux.create Contrôle manuel de l’émission des événements Flowable.using Flux.using Gestion d’une ressource Flowable.fromPublisher Flux.from Utilisation d’un Publisher (Reactive Streams) Flowable.generate Flux.generate Utilisation d’un générateur de valeurs
  121. RxJava
 RxJava 2 Reactor Flowable.just Flux.just Émission d’une valeur existante

    Flowable.defer Flux.defer Émission tardive d’un Observable Flowable.fromCallable Mono.fromCallable Émission à partir d’un appel de fonction Flowable.create Flux.create Contrôle manuel de l’émission des événements Flowable.using Flux.using Gestion d’une ressource Flowable.fromPublisher Flux.from Utilisation d’un Publisher (Reactive Streams) Flowable.generate Flux.generate Utilisation d’un générateur de valeurs
  122. RxJava
 RxJava 2 Reactor Flowable.just Flux.just Émission d’une valeur existante

    Flowable.defer Flux.defer Émission tardive d’un Observable Flowable.fromCallable Mono.fromCallable Émission à partir d’un appel de fonction Flowable.create Flux.create Contrôle manuel de l’émission des événements Flowable.using Flux.using Gestion d’une ressource Flowable.fromPublisher Flux.from Utilisation d’un Publisher (Reactive Streams) Flowable.generate Flux.generate Utilisation d’un générateur de valeurs
  123. RxJava
 RxJava 2 Reactor Flowable.just Flux.just Émission d’une valeur existante

    Flowable.defer Flux.defer Émission tardive d’un Observable Flowable.fromCallable Mono.fromCallable Émission à partir d’un appel de fonction Flowable.create Flux.create Contrôle manuel de l’émission des événements Flowable.using Flux.using Gestion d’une ressource Flowable.fromPublisher Flux.from Utilisation d’un Publisher (Reactive Streams) Flowable.generate Flux.generate Utilisation d’un générateur de valeurs
  124. RxJava
 RxJava 2 Reactor Flowable.just Flux.just Émission d’une valeur existante

    Flowable.defer Flux.defer Émission tardive d’un Observable Flowable.fromCallable Mono.fromCallable Émission à partir d’un appel de fonction Flowable.create Flux.create Contrôle manuel de l’émission des événements Flowable.using Flux.using Gestion d’une ressource Flowable.fromPublisher Flux.from Utilisation d’un Publisher (Reactive Streams) Flowable.generate Flux.generate Utilisation d’un générateur de valeurs
  125. exemple d’encapsulation

  126. @RestController
 public class HelloController {
 
 private static final byte[]

    TOPIC_NAME = "topic".getBytes();
 
 @RequestMapping(value = "/redis")
 private String redis() throws InterruptedException {
 CountDownLatch latch = new CountDownLatch(1);
 AtomicReference<String> result = new AtomicReference<>();
 this.connection.subscribe((message, pattern) -> {
 result.set(message.toString());
 latch.countDown();
 }, TOPIC_NAME);
 latch.await();
 return result.get();
 } }
  127. @RestController
 public class HelloController {
 
 private static final byte[]

    TOPIC_NAME = "topic".getBytes();
 
 @RequestMapping(value = "/redis")
 private String redis() throws InterruptedException {
 CountDownLatch latch = new CountDownLatch(1);
 AtomicReference<String> result = new AtomicReference<>();
 this.connection.subscribe((message, pattern) -> {
 result.set(message.toString());
 latch.countDown();
 }, TOPIC_NAME);
 latch.await();
 return result.get();
 } } Écoute de Redis
  128. @RestController
 public class HelloController {
 
 private static final byte[]

    TOPIC_NAME = "topic".getBytes();
 
 @RequestMapping(value = "/redis")
 private String redis() throws InterruptedException {
 CountDownLatch latch = new CountDownLatch(1);
 AtomicReference<String> result = new AtomicReference<>();
 this.connection.subscribe((message, pattern) -> {
 result.set(message.toString());
 latch.countDown();
 }, TOPIC_NAME);
 latch.await();
 return result.get();
 } } Code de synchronisation Code de synchronisation
  129. Étape 1 Encapsuler

  130. @RestController
 public class HelloController {
 
 private static final byte[]

    TOPIC_NAME = "topic".getBytes();
 
 @RequestMapping(value = "/redis")
 private String redis() throws InterruptedException {
 
 String result = Flowable.create(sub -> {
 this.connection.subscribe((message, pattern) -> {
 sub.onNext(message.toString());
 sub.onComplete();
 }, TOPIC_NAME);
 }, BackpressureStrategy.BUFFER)
 .blockingFirst();
 return result;
 } }
  131. @RestController
 public class HelloController {
 
 private static final byte[]

    TOPIC_NAME = "topic".getBytes();
 
 @RequestMapping(value = "/redis")
 private String redis() throws InterruptedException {
 
 String result = Flowable.create(sub -> {
 this.connection.subscribe((message, pattern) -> {
 sub.onNext(message.toString());
 sub.onComplete();
 }, TOPIC_NAME);
 }, BackpressureStrategy.BUFFER)
 .blockingFirst();
 return result;
 } } Encapsulation Synchronisation Contract Reactive
  132. Étape 2 Asynchrone

  133. @RestController
 public class HelloController {
 
 private static final byte[]

    TOPIC_NAME = "topic".getBytes();
 
 @RequestMapping(value = "/redis")
 private DeferredResult<String> redis() throws InterruptedException {
 
 DeferredResult<String> result = new DeferredResult<>(10_000l);
 
 Flowable.create(sub -> {
 this.connection.subscribe((message, pattern) -> {
 sub.onNext(message.toString());
 sub.onComplete();
 }, TOPIC_NAME);
 }, BackpressureStrategy.BUFFER)
 .subscribe(result::setResult);
 
 return result;
 }
  134. @RestController
 public class HelloController {
 
 private static final byte[]

    TOPIC_NAME = "topic".getBytes();
 
 @RequestMapping(value = "/redis")
 private DeferredResult<String> redis() throws InterruptedException {
 
 DeferredResult<String> result = new DeferredResult<>(10_000l);
 
 Flowable.create(sub -> {
 this.connection.subscribe((message, pattern) -> {
 sub.onNext(message.toString());
 sub.onComplete();
 }, TOPIC_NAME);
 }, BackpressureStrategy.BUFFER)
 .subscribe(result::setResult);
 
 return result;
 } Résultat tardif Utilisation de DeferredResult
  135. Étape 3 Reactive Streams

  136. @RestController
 public class HelloController {
 
 private static final byte[]

    TOPIC_NAME = "topic".getBytes();
 
 @RequestMapping(value = "/redis", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
 private Flux<String> redis() throws InterruptedException {
 
 Flowable<String> rxjava = Flowable.create(sub -> {
 this.connection.subscribe((message, pattern) -> sub.onNext(message.toString()), TOPIC_NAME);
 }, BackpressureStrategy.BUFFER);
 
 return Flux.defer(() -> rxjava);
 } }
  137. @RestController
 public class HelloController {
 
 private static final byte[]

    TOPIC_NAME = "topic".getBytes();
 
 @RequestMapping(value = "/redis", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
 private Flux<String> redis() throws InterruptedException {
 
 Flowable<String> rxjava = Flowable.create(sub -> {
 this.connection.subscribe((message, pattern) -> sub.onNext(message.toString()), TOPIC_NAME);
 }, BackpressureStrategy.BUFFER);
 
 return Flux.defer(() -> rxjava);
 } } RxJava 2 → Flux Flux → SSE
  138. @RestController
 public class HelloController {
 
 private static final byte[]

    TOPIC_NAME = "topic".getBytes();
 
 @RequestMapping(value = "/redis", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
 private Publisher<String> redis() throws InterruptedException {
 return Flowable.create(sub -> {
 this.connection.subscribe((message, pattern) -> sub.onNext(message.toString()), TOPIC_NAME);
 }, BackpressureStrategy.BUFFER);
 
 }
  139. @RestController
 public class HelloController {
 
 private static final byte[]

    TOPIC_NAME = "topic".getBytes();
 
 @RequestMapping(value = "/redis", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
 private Publisher<String> redis() throws InterruptedException {
 return Flowable.create(sub -> {
 this.connection.subscribe((message, pattern) -> sub.onNext(message.toString()), TOPIC_NAME);
 }, BackpressureStrategy.BUFFER);
 
 } Publisher
  140. None
  141. Migrer son application Reactive depuis RxJava

  142. rx.Observable io.reactivex.Observable rx.Single rx.Completable io.reactivex.Completable io.reactivex.Single RxJava RxJava 2 package

    différent
  143. RxJavaInterop.toV2Observable(rx.Observable.just(1, 2, 3))
 .firstElement()
 .subscribe();
 
 RxJavaInterop.toV1Observable(io.reactivex.Observable.just(1, 2, 3), 


    BackpressureStrategy.DROP)
 .first()
 .subscribe(); https://github.com/akarnokd/RxJava2Interop RxJava → RxJava 2 RxJava 2 → RxJava
  144. Reactive Streams n’ acceptent pas de valeur null (sauf Maybe.fromCallable)

  145. Observable.just(null);
 
 Single.just(null);
 
 Observable.fromCallable(() -> null)
 .subscribe(System.out::println, Throwable::printStackTrace);
 


    Observable.just(1).map(v -> null)
 .subscribe(System.out::println, Throwable::printStackTrace); NullPointerException NullPointerException NullPointerException N ullPointerException
  146. Reactor se base sur Java 8 tandis que RxJava 2

    utilise Java 6
  147. Reactor se base sur Java 8 tandis que RxJava 2

    utilise Java 6
  148. Flux.interval(Duration.of(1, ChronoUnit.SECONDS))
 .map(i -> String.valueOf(i))
 .subscribe();

  149. Flux.interval(Duration.of(1, ChronoUnit.SECONDS))
 .map(i -> String.valueOf(i))
 .subscribe(); java.time.Duration java.util.Function

  150. Flux.fromStream(stream)
 .map(i -> "value -> " + String.valueOf(i))
 .toStream()
 .forEach(System.out::println);

  151. Flux.fromStream(stream)
 .map(i -> "value -> " + String.valueOf(i))
 .toStream()
 .forEach(System.out::println);

    Conversion Stream Java 8 en Flux Conversion Flux en Stream Java 8
  152. Reactor se base sur Java 8 tandis que RxJava 2

    utilise Java 6
  153. Reactor se base sur Java 8 tandis que RxJava 2

    utilise Java 6
  154. rx.Action1 rx.Func1 RxJava RxJava 2 rx.Func2 rx.Func3 rx.Action2 io.reactivex.functions.Consumer io.reactivex.functions.Function

    io.reactivex.functions.BiFunction io.reactivex.functions.Function3 io.reactivex.functions.BiConsumer Suivre les conventions Java 8
  155. Observable.interval(1, TimeUnit.SECONDS)
 .map(i -> String.valueOf(i))
 .subscribe();

  156. Observable.interval(1, TimeUnit.SECONDS)
 .map(i -> String.valueOf(i))
 .subscribe(); java.util.concurrent.TimeUnit

  157. Observable.interval(1, TimeUnit.SECONDS)
 .map(i -> String.valueOf(i))
 .subscribe(); io.reactivex.functions.Function

  158. Asynchrone gestion de contexte d’exécution

  159. Schedulers Rethink cette partie là

  160. Scheduler 
 (Pool de Thread) Tâche 1 Tâche 2 Tâche

    3 Tâche 4
  161. Tâche 1 Tâche 2 Tâche 3 Tâche 4 Scheduler 


    (Pool de Thread)
  162. Tâche 1 Tâche 2 Tâche 3 Tâche 4 Scheduler 


    (Pool de Thread)
  163. Tâche 1 Tâche 2 Tâche 3 Tâche 4 Scheduler 


    (Pool de Thread)
  164. Tâche 1 Tâche 2 Tâche 3 Tâche 4 Scheduler 


    (Pool de Thread)
  165. Tâche 1 Tâche 2 Tâche 3 Tâche 4 Scheduler 


    (Pool de Thread)
  166. Tâche 1 Tâche 2 Tâche 3 Tâche 4 Scheduler 


    (Pool de Thread)
  167. Tâche 1 Tâche 2 Tâche 3 Tâche 4 Scheduler 


    (Pool de Thread)
  168. Tâche 1 Tâche 2 Tâche 3 Tâche 4 Scheduler 


    (Pool de Thread)
  169. Scheduler 
 (Single Thread) Tâche 1 Tâche 2 Tâche 3

    Tâche 4
  170. Scheduler 
 (Single Thread) Tâche 1 Tâche 2 Tâche 3

    Tâche 4
  171. Scheduler 
 (Single Thread) Tâche 1 Tâche 2 Tâche 3

    Tâche 4
  172. Scheduler 
 (Single Thread) Tâche 1 Tâche 2 Tâche 3

    Tâche 4
  173. Scheduler 
 (Single Thread) Tâche 1 Tâche 2 Tâche 3

    Tâche 4
  174. Scheduler 
 (Single Thread) Tâche 1 Tâche 2 Tâche 3

    Tâche 4
  175. Scheduler 
 (Single Thread) Tâche 1 Tâche 2 Tâche 3

    Tâche 4
  176. Scheduler 
 (Single Thread) Tâche 1 Tâche 2 Tâche 3

    Tâche 4
  177. Scheduler 
 (Single Thread) Tâche 1 Tâche 2 Tâche 3

    Tâche 4
  178. java.lang.IllegalStateException: 
 Not on the main thread NetworkOnMainThreadException

  179. JavaFx.fromClick(btn) 
 .observeOn(Schedulers.io())
 .switchMap(evt -> remoteApi.getData())
 .observeOn(Schedulers.computation())
 .flatMap(data -> intensiveComputation(data))


    .observeOn(javaFx())
 .doOnNext(value -> btn.setText("Data: " + value)) 
 .subscribe();
  180. JavaFx.fromClick(btn) 
 .observeOn(Schedulers.io())
 .switchMap(evt -> remoteApi.getData())
 .observeOn(Schedulers.computation())
 .flatMap(data -> intensiveComputation(data))


    .observeOn(javaFx())
 .doOnNext(value -> btn.setText("Data: " + value)) 
 .subscribe(); entrée/sortie calculs Thread graphique
  181. RxJava RxJava 2 Reactor Description io() io() elastic() Pool de

    Thread qui grossit au besoin computation() computation() parallel() Pool fini de Thread single() single() single() Pool de 1 Thread immediate() immediate() Execute la tâche immédiatement trampoline() trampoline() Ajoute la tâche dans une file d’attente avant de l’executer
  182. RxJava RxJava 2 Reactor Description io() io() elastic() Pool de

    Thread qui grossit au besoin computation() computation() parallel() Pool fini de Thread single() single() single() Pool de 1 Thread immediate() immediate() Execute la tâche immédiatement trampoline() trampoline() Ajoute la tâche dans une file d’attente avant de l’executer
  183. RxJava RxJava 2 Reactor Description io() io() elastic() Pool de

    Thread qui grossit au besoin computation() computation() parallel() Pool fini de Thread single() single() single() Pool de 1 Thread immediate() immediate() Execute la tâche immédiatement trampoline() trampoline() Ajoute la tâche dans une file d’attente avant de l’executer
  184. RxJava RxJava 2 Reactor Description io() io() elastic() Pool de

    Thread qui grossit au besoin computation() computation() parallel() Pool fini de Thread single() single() single() Pool de 1 Thread immediate() immediate() Execute la tâche immédiatement trampoline() trampoline() Ajoute la tâche dans une file d’attente avant de l’executer
  185. RxJava RxJava 2 Reactor Description io() io() elastic() Pool de

    Thread qui grossit au besoin computation() computation() parallel() Pool fini de Thread single() single() single() Pool de 1 Thread immediate() immediate() Execute la tâche immédiatement trampoline() trampoline() Ajoute la tâche dans une file d’attente avant de l’executer Absent pour votre bien
  186. RxJava RxJava 2 Reactor Description io() io() elastic() Pool de

    Thread qui grossit au besoin computation() computation() parallel() Pool fini de Thread single() single() single() Pool de 1 Thread immediate() immediate() Execute la tâche immédiatement trampoline() trampoline() Ajoute la tâche dans une file d’attente avant de l’executer
  187. Reactor nommage technique

  188. RxJava nommage fonctionnel

  189. Backpressure

  190. Consommateur ça déborde !

  191. Flowable.range(1, 100000)
 .subscribe(new Subscriber<Integer>() {
 
 private Subscription source;
 


    @Override
 public void onSubscribe(Subscription s) {
 this.source = s;
 this.source.request(1);
 }
 
 @Override
 public void onNext(Integer o) {
 longComputation(o);
 this.source.request(1);
 }
 });
  192. Flowable.range(1, 100000)
 .subscribe(new Subscriber<Integer>() {
 
 private Subscription source;
 


    @Override
 public void onSubscribe(Subscription s) {
 this.source = s;
 this.source.request(1);
 }
 
 @Override
 public void onNext(Integer o) {
 longComputation(o);
 this.source.request(1);
 }
 }); Garder la référence de l’abonnement Demander l’émission des éléments suivants Demande l’émission des premiers éléments
  193. Flowable.range(1, 100000)
 .subscribe(new Subscriber<Integer>() {
 
 private Subscription source;
 


    @Override
 public void onSubscribe(Subscription s) {
 this.source = s;
 this.source.request(1);
 }
 
 @Override
 public void onNext(Integer o) {
 longComputation(o);
 this.source.request(1);
 }
 }); Contrôle de l’émission
  194. source()
 .subscribe(new Subscriber<Integer>() {
 
 private Subscription source;
 
 @Override


    public void onSubscribe(Subscription s) {
 this.source = s;
 this.source.request(1);
 }
 
 @Override
 public void onNext(Integer o) {
 longComputation(o);
 this.source.request(1);
 }
 }); Kafka JDBC Fichier
  195. La mécanique est identique avec Reactor (merci les Reactive Streams)

  196. Debug Rajouter stack insignifiant

  197. rx.exceptions.OnErrorNotImplementedException: Sequence contains too many elements at rx.internal.util.InternalObservableUtils$ErrorNotImplementedAction.call(InternalObservableUtils.java: 386) at

    rx.internal.util.InternalObservableUtils$ErrorNotImplementedAction.call(InternalObservableUtils.java: 383) at rx.internal.util.ActionSubscriber.onError(ActionSubscriber.java:44) at rx.observers.SafeSubscriber._onError(SafeSubscriber.java:153) at rx.observers.SafeSubscriber.onError(SafeSubscriber.java:115) at rx.internal.operators.OperatorSingle$ParentSubscriber.onNext(OperatorSingle.java:97) at rx.internal.operators.OnSubscribeRange$RangeProducer.slowPath(OnSubscribeRange.java:90) at rx.internal.operators.OnSubscribeRange$RangeProducer.request(OnSubscribeRange.java:68) at rx.Subscriber.setProducer(Subscriber.java:211) at rx.internal.operators.OnSubscribeRange.call(OnSubscribeRange.java:38) Stacktrace inexploitable
  198. RxJavaHooks.enableAssemblyTracking(); […] Caused by: rx.exceptions.AssemblyStackTraceException: Assembly trace: at rx.Observable.create(Observable.java:100) at

    rx.Observable.lift(Observable.java:237) at rx.Observable.single(Observable.java:9330) at rxjava2.BasicExampleTest.stacktrace(BasicExampleTest.java:225) rx.Observable.range(1, 100)
 .single()
 .subscribe();
  199. enableTracing n’est pas encore supporté par RxJava 2 (pour cela,

    il faut utiliser RxJava2Extensions) https://github.com/akarnokd/RxJava2Extensions
  200. Hooks.onOperator(providedHook -> providedHook.operatorStacktrace());
 
 Flux.just(1, 2, 3)
 .single()
 .subscribe(System.out::println, Throwable::printStackTrace);

  201. Hooks.onOperator(providedHook -> providedHook.operatorStacktrace());
 
 Flux.just(1, 2, 3)
 .single()
 .subscribe(System.out::println, Throwable::printStackTrace);

  202. Flux.range(1, 100)
 .single()
 .checkpoint("label")
 .subscribe(…);

  203. Flux.range(1, 100)
 .single()
 .checkpoint("label")
 .subscribe(…);

  204. Flux.range(1, 100)
 .single()
 .checkpoint("label")
 .subscribe(…); Assembly trace from producer [reactor.core.publisher.MonoSingle],

    described as [label] : reactor.core.publisher.Mono.checkpoint(Mono.java:1304) rxjava2.BasicExampleTest.stacktraceReactor(BasicExampleTest.java:236) Error has been observed by the following operator(s): |_ Mono.checkpoint(BasicExampleTest.java:236)
  205. Attention dégradation des performances !

  206. Performance

  207. Génération 3 4 5+ Fusion RxJava 2 Reactor

  208. Attention Slides conceptuels

  209. Sans fusion

  210. Transform (  ⬛ → ⃝ ) Transform ( ⃝

    →  ⬛)
  211. Transform (  ⬛ → ⃝ ) Transform ( ⃝

    →  ⬛)
  212. Transform (  ⬛ → ⃝ ) Transform ( ⃝

    →  ⬛)
  213. Transform (  ⬛ → ⃝ ) Transform ( ⃝

    →  ⬛)
  214. Transform (  ⬛ → ⃝ ) Transform ( ⃝

    →  ⬛)
  215. Transform (  ⬛ → ⃝ ) Transform ( ⃝

    →  ⬛)
  216. Avec fusion

  217. Transform (  ⬛ → ⃝ ) Transform ( ⃝

    →  ⬛)
  218. Transform (  ⬛ → ⃝ ) Transform ( ⃝

    →  ⬛)
  219. Transform (  ⬛ → ⃝ ) Transform ( ⃝

    →  ⬛)
  220. Transform (  ⬛ → ⃝ ) Transform ( ⃝

    →  ⬛)
  221. Transform (  ⬛ → ⃝ ) Transform ( ⃝

    →  ⬛)
  222. La fusion diminue la consommation mémoire et augmente les performances

  223. for (int x = 0; x < 10_000; x++) {


    
 Observable.interval(10, TimeUnit.MILLISECONDS)
 .takeWhile(i -> take.get())
 .flatMap(i -> Observable.range(1, 100))
 .subscribe();
 }
  224. RxJava 2 RxJava Reactor

  225. http://akarnokd.blogspot.fr/2016/12/the-reactive-scrabble-benchmarks.html Stream Java 8 RxJava 2 / Reactor RxJava Décembre

    2016
  226. Stream Java 8 RxJava 2 / Reactor RxJava https://twitter.com/akarnokd/status/844555409012740096 Mars

    2017
  227. RxJava 2 : 
 streams performants pour Android

  228. Reactor & RxJava 2 : 
 streams performants pour serveur

    web
  229. Ecosystème

  230. Communauté Sponsor RxJava Grande Anciennement Netflix
 Maintenant Community RxJava 2

    Grande Community Reactor En croissance Pivotal
  231. RxJava RxJava 2 Reactor Retrofit Oui Oui Non RxAndroid Oui

    Oui Non Realm Oui Non Non Hystrix Oui Non Non Couchbase Oui Non Non MongoDB Oui Non Non Spring Data 2.0 Oui Non Oui Reactor IPC Non Non Oui WebFlux Non Oui Oui Android Spring
  232. RxJava RxJava 2 Reactor Retrofit Oui Oui Non RxAndroid Oui

    Oui Non Realm Oui Non Non Hystrix Oui Non Non Couchbase Oui Non Non MongoDB Oui Non Non Spring Data 2.0 Oui Non Oui Reactor IPC Non Non Oui WebFlux Non Oui Oui Android Spring
  233. We are aggressively 
 migrating our internal code to RxJava

    2 https://github.com/uber/AutoDispose
  234. RxJava RxJava 2 Reactor Retrofit Oui Oui Non RxAndroid Oui

    Oui Non Realm Oui Non Non Hystrix Oui Non Non Couchbase Oui Non Non MongoDB Oui Non Non Spring Data 2.0 Oui Non Oui Reactor IPC Non Non Oui WebFlux Non Oui Oui Android Spring genre pourquoi si tu as une stack spring se forcer a faire du rx2 si le support reactor est meilleur)?
  235. Forte inertie pour migrer

  236. Migrer vers RxJava 2 ou Reactor

  237. RxJava RxJava 2 Reactor Retrofit Oui Oui Non RxAndroid Oui

    Oui Non Realm Oui Non Non Hystrix Oui Non Non Couchbase Oui Non Non MongoDB Oui Non Non Spring Data 2.0 Oui Non Oui Reactor IPC Non Non Oui WebFlux Non Oui Oui Android Spring
  238. Spring 5 va accelerer l’adoption de Reactor

  239. None
  240. None
  241. Android RxJava 2

  242. Backend RxJava 2

  243. Spring Reactor

  244. 2017 l’année des Reactive Streams

  245. Merci pour votre attention On reste en contact ? david.wursteisen@gmail.com

    @dwursteisen http://blog.soat.fr RefCard RxJava 2 bientôt disponible !