Architecture découplée grâce aux Reactive Extensions

7843bb075c05be6886a97b77e36758ff?s=47 David
April 20, 2016

Architecture découplée grâce aux Reactive Extensions

Auparavant, les applications se connectaient à une seule base de données. Aujourd’hui, elles doivent communiquer avec un ensemble de base de données et composer avec des services web distants. Elles ne sont plus des blocs monolithiques, mais distribuées sur différents serveurs sur différentes zones géographiques. Elles sont “scalables” mais elles doivent maintenant attendre les réponses d’autres applications.

Comment profiter de ces temps d’attente ? Comment casser ce couplage temporel ? Comment transformer un couplage applicatif fort en couplage lâche ?

Cette université fera le tour de RxJava et RxJs. Comment les mettre en place sur un projet et dans une équipe. Et surtout comment utiliser ces bibliothèques pour découpler votre architecture.

University - 20 Avril 2016 - Devoxx France (Palais des congrès de Paris)

David Wursteisen - Brice Dutheil

7843bb075c05be6886a97b77e36758ff?s=128

David

April 20, 2016
Tweet

Transcript

  1. #DevoxxFR #Rx Architecture Découplée grâce aux Reactive Extensions @BriceDutheil @dwursteisen

    Mercredi 20 Avril - Amphi bleu - 9h30
  2. @BriceDutheil @dwursteisen #DevoxxFR #Rx Ce que nous allons voir

  3. @BriceDutheil @dwursteisen #DevoxxFR #Rx Ce que nous allons voir Reactive

    Extensions en théorie
  4. @BriceDutheil @dwursteisen #DevoxxFR #Rx Ce que nous allons voir Reactive

    Extensions en théorie en pratique
  5. @BriceDutheil @dwursteisen #DevoxxFR #Rx Ce que nous allons voir Reactive

    Extensions en théorie en pratique sur notre application
  6. @BriceDutheil @dwursteisen #DevoxxFR #Rx Ce que nous allons voir Reactive

    Extensions en théorie en pratique sur notre application Pause
  7. @BriceDutheil @dwursteisen #DevoxxFR #Rx Ce que nous ne verrons pas

  8. @BriceDutheil @dwursteisen #DevoxxFR #Rx Ce que nous ne verrons pas

    Énumération des opérateurs
  9. @BriceDutheil @dwursteisen #DevoxxFR #Rx Ce que nous ne verrons pas

    Énumération des opérateurs Mieux que ces alternatives ?
  10. @BriceDutheil @dwursteisen #DevoxxFR #Rx Ce que nous ne verrons pas

    Énumération des opérateurs Mieux que ces alternatives ? Expliquer tous les patterns d’utilisation possible
  11. @BriceDutheil @dwursteisen #DevoxxFR #Rx Qui sommes nous ?

  12. @BriceDutheil @dwursteisen #DevoxxFR #Rx Indép’ Ingénierie logicielle

  13. @BriceDutheil @dwursteisen #DevoxxFR #Rx Formation Conseil Consultant

  14. @BriceDutheil @dwursteisen #DevoxxFR #Rx https://youtu.be/TT8TLS2Ac0o

  15. @BriceDutheil @dwursteisen #DevoxxFR #Rx #DevoxxFR #Rx

  16. @BriceDutheil @dwursteisen #DevoxxFR #Rx Êtes-vous familier avec RxJava ?

  17. @BriceDutheil @dwursteisen #DevoxxFR #Rx Êtes-vous familier avec RxJava ? Oui

    Non
  18. @BriceDutheil @dwursteisen #DevoxxFR #Rx Êtes-vous familier avec RxJava ? Oui

  19. @BriceDutheil @dwursteisen #DevoxxFR #Rx Êtes-vous familier avec RxJava ? Non

  20. @BriceDutheil @dwursteisen #DevoxxFR #Rx Architecture Découplée grâce aux Reactive Extensions

  21. @BriceDutheil @dwursteisen #DevoxxFR #Rx Le couplage temporel

  22. @BriceDutheil @dwursteisen #DevoxxFR #Rx Application typique war tomcat

  23. @BriceDutheil @dwursteisen #DevoxxFR #Rx Connaissez vous le fonctionnement de Tomcat

    ?
  24. @BriceDutheil @dwursteisen #DevoxxFR #Rx Connaissez vous Tomcat ? Oui Non

  25. @BriceDutheil @dwursteisen #DevoxxFR #Rx Connaissez vous Tomcat ? Oui

  26. @BriceDutheil @dwursteisen #DevoxxFR #Rx Connaissez vous Tomcat ? Non

  27. @BriceDutheil @dwursteisen #DevoxxFR #Rx Examinons le cheminement d’une requête HTTP

  28. @BriceDutheil @dwursteisen #DevoxxFR #Rx client nouvelle requête

  29. @BriceDutheil @dwursteisen #DevoxxFR #Rx client OS Négociation TCP nouvelle requête

  30. @BriceDutheil @dwursteisen #DevoxxFR #Rx Tomcat / BIO client OS Négociation

    TCP Requête # nouvelle requête connexion établie prête à être consommée
  31. @BriceDutheil @dwursteisen #DevoxxFR #Rx Tomcat / BIO client OS Négociation

    TCP Requête # nouvelle requête Acceptor Thread Accepte la requête connexion établie prête à être consommée acceptedCount =
  32. @BriceDutheil @dwursteisen #DevoxxFR #Rx Tomcat / BIO client OS Négociation

    TCP Requête # nouvelle requête Acceptor Thread connexion établie prête à être consommée Accepte la requête Requête donnée aux threads de travail si slot disponible Worker Executor maxThreads =
  33. @BriceDutheil @dwursteisen #DevoxxFR #Rx Tomcat / BIO client OS Négociation

    TCP Requête # nouvelle requête Acceptor Thread connexion établie prête à être consommée Accepte la requête Requête donnée aux threads de travail si slot disponible Thread
  34. @BriceDutheil @dwursteisen #DevoxxFR #Rx Tomcat / BIO client OS Négociation

    TCP Requête # nouvelle requête Acceptor Thread connexion établie prête à être consommée Accepte la requête Requête donnée aux threads de travail si slot disponible Thread w a r Lit la payload war
  35. @BriceDutheil @dwursteisen #DevoxxFR #Rx Tomcat / BIO client OS Négociation

    TCP Requête # nouvelle requête Acceptor Thread connexion établie prête à être consommée Accepte la requête Requête donnée aux threads de travail si slot disponible Thread w a r Lit la payload Business war
  36. @BriceDutheil @dwursteisen #DevoxxFR #Rx Tomcat / BIO client OS Négociation

    TCP Requête # nouvelle requête Acceptor Thread connexion établie prête à être consommée Accepte la requête Requête donnée aux threads de travail si slot disponible Thread w a r Lit la payload Écrit la réponse, ferme la connexion Business war
  37. @BriceDutheil @dwursteisen #DevoxxFR #Rx Tomcat / BIO client OS Négociation

    TCP Requête # nouvelle requête Acceptor Thread connexion établie prête à être consommée Accepte la requête Requête donnée aux threads de travail si slot disponible Thread war Lit la payload Écrit la réponse, ferme la connexion Business
  38. @BriceDutheil @dwursteisen #DevoxxFR #Rx Premier constat Files d’attente OS

  39. @BriceDutheil @dwursteisen #DevoxxFR #Rx Un thread par requête

  40. @BriceDutheil @dwursteisen #DevoxxFR #Rx Plus de thread Plus gros hardware

  41. @BriceDutheil @dwursteisen #DevoxxFR #Rx Plus de thread Plus de serveurs

  42. @BriceDutheil @dwursteisen #DevoxxFR #Rx Application typique war tomcat service tiers

  43. @BriceDutheil @dwursteisen #DevoxxFR #Rx Avec une base de données

  44. @BriceDutheil @dwursteisen #DevoxxFR #Rx Tomcat client OS Négociation TCP Requête

    # nouvelle requête Acceptor Thread connexion établie prête à être consommée Accepte la requête Requête donnée aux threads de travail si slot disponible Thread w a r Lit la payload Écrit la réponse, ferme la connexion Business war
  45. @BriceDutheil @dwursteisen #DevoxxFR #Rx Client Nouvelle requête Thread war Temps

  46. @BriceDutheil @dwursteisen #DevoxxFR #Rx Client Nouvelle requête Thread war Plus

    de requêtes en base Plus long à répondre
  47. @BriceDutheil @dwursteisen #DevoxxFR #Rx Client Nouvelle requête Thread webservice.api microservice

    # war Avec d’autres dépendances
  48. @BriceDutheil @dwursteisen #DevoxxFR #Rx Tomcat client OS Requête # nouvelle

    requête Acceptor Thread Attend un slot disponible Thread war Executor full
  49. @BriceDutheil @dwursteisen #DevoxxFR #Rx Point de vue du CPU Pas

    d’instruction à exécuter Le système attend sur des I/Os
  50. @BriceDutheil @dwursteisen #DevoxxFR #Rx Deuxième observation : Couplage temporel

  51. @BriceDutheil @dwursteisen #DevoxxFR #Rx On attend Services distants Base de

    données Fournisseurs tiers
  52. @BriceDutheil @dwursteisen #DevoxxFR #Rx Localement aussi Lecture/Écriture sur disque -

    ex : swap Panne hardware - ex : disque en panne augmente la latence
  53. @BriceDutheil @dwursteisen #DevoxxFR #Rx Solutions ?

  54. @BriceDutheil @dwursteisen #DevoxxFR #Rx Augmenter le nombre de thread Tomcat

  55. @BriceDutheil @dwursteisen #DevoxxFR #Rx Utiliser d’autres Executor

  56. @BriceDutheil @dwursteisen #DevoxxFR #Rx C’est toujours bloquant et ça peut

    devenir complexe
  57. @BriceDutheil @dwursteisen #DevoxxFR #Rx Revient à augmenter le nombre de

    thread
  58. @BriceDutheil @dwursteisen #DevoxxFR #Rx Sur Tomcat, utiliser un autre connecteur

    ? NIO, APR, NIO2 (Tomcat 8)
  59. @BriceDutheil @dwursteisen #DevoxxFR #Rx NIO ne résout pas le problème

    de fond §9.2 The Container Provider should ensure that the dispatch of the request to a target servlet occurs in the same thread of the same JVM as the original request. http://download.oracle.com/otn-pub/jcp/servlet-3.0-mrel-full-oth-JSpec/servlet-3_0-mrel-spec.pdf
  60. @BriceDutheil @dwursteisen #DevoxxFR #Rx Tomcat / NIO client OS Requête

    # nouvelle requête Acceptor Thread Thread Poller Thread Requête # war
  61. @BriceDutheil @dwursteisen #DevoxxFR #Rx Tomcat / NIO client OS Requête

    # nouvelle requête Acceptor Thread Thread Poller Thread Requête # war
  62. @BriceDutheil @dwursteisen #DevoxxFR #Rx Ne pas oublier le tuning

  63. @BriceDutheil @dwursteisen #DevoxxFR #Rx Ne pas oublier le tuning mais…

  64. @BriceDutheil @dwursteisen #DevoxxFR #Rx Comprendre la JVM

  65. @BriceDutheil @dwursteisen #DevoxxFR #Rx Long à établir

  66. @BriceDutheil @dwursteisen #DevoxxFR #Rx Change avec la charge / le

    business
  67. @BriceDutheil @dwursteisen #DevoxxFR #Rx Dans tous les cas Tomcat

  68. @BriceDutheil @dwursteisen #DevoxxFR #Rx Dans tous les cas Notre application

  69. @BriceDutheil @dwursteisen #DevoxxFR #Rx

  70. @BriceDutheil @dwursteisen #DevoxxFR #Rx Améliorer le code de notre application

  71. @BriceDutheil @dwursteisen #DevoxxFR #Rx Améliorer le code de notre application

    • Executors • Future
  72. @BriceDutheil @dwursteisen #DevoxxFR #Rx Améliorer le code de notre application

    • Executors • Future • Embarquer une techno à la ActiveMQ ?
  73. @BriceDutheil @dwursteisen #DevoxxFR #Rx Améliorer le code de notre application

    • Executors • Future • Embarquer une techno à la ActiveMQ ? • Problème de lourdeur, overhead
  74. @BriceDutheil @dwursteisen #DevoxxFR #Rx Améliorer le code de notre application

    • Executors • Future • Embarquer une techno à la ActiveMQ ? • Problème de lourdeur, overhead • Quid de la composition
  75. @BriceDutheil @dwursteisen #DevoxxFR #Rx Améliorer le code de notre application

    • Executors • Future • Embarquer une techno à la ActiveMQ ? • Problème de lourdeur, overhead • Quid de la composition • CompletableFuture => Avec Collections ou Streams ?
  76. @BriceDutheil @dwursteisen #DevoxxFR #Rx Améliorer le code de notre application

    • Executors • Future • Embarquer une techno à la ActiveMQ ? • Problème de lourdeur, overhead • Quid de la composition • CompletableFuture => Avec Collections ou Streams ?
  77. @BriceDutheil @dwursteisen #DevoxxFR #Rx Améliorer le code de notre application

    • Executors • Future • Embarquer une techno à la ActiveMQ ? • Problème de lourdeur, overhead • Quid de la composition • CompletableFuture => Avec Collections ou Streams ? • Gestion d’erreurs
  78. @BriceDutheil @dwursteisen #DevoxxFR #Rx Le code pour mitiger ces problèmes

    est complexe
  79. @BriceDutheil @dwursteisen #DevoxxFR #Rx Le code pour mitiger ces problèmes

    est plus obscur au raisonnement
  80. @BriceDutheil @dwursteisen #DevoxxFR #Rx Avez vous déjà rencontré ces problèmes

    ? Oui Non
  81. @BriceDutheil @dwursteisen #DevoxxFR #Rx Avez vous déjà rencontré ces problèmes

    ? Non
  82. @BriceDutheil @dwursteisen #DevoxxFR #Rx Avez vous déjà rencontré ces problèmes

    ? Oui
  83. @BriceDutheil @dwursteisen #DevoxxFR #Rx Quelles solutions ? Tuning Tomcat Pool

    JDBC … Changement du code
  84. @BriceDutheil @dwursteisen #DevoxxFR #Rx Quelles solutions ? Tuning Tomcat Pool

    JDBC …
  85. @BriceDutheil @dwursteisen #DevoxxFR #Rx Quelle solutions ? Changement du code

  86. @BriceDutheil @dwursteisen #DevoxxFR #Rx Reactive Extensions pour mieux utiliser les

    ressources hardware
  87. #DevoxxFR #Rx Reactive Extensions En théorie

  88. @BriceDutheil @dwursteisen #DevoxxFR #Rx Historique

  89. @BriceDutheil @dwursteisen #DevoxxFR #Rx Historique

  90. @BriceDutheil @dwursteisen #DevoxxFR #Rx Historique .NET Java JavaScript

  91. @BriceDutheil @dwursteisen #DevoxxFR #Rx Historique .NET Java JavaScript Scala Clojure

    Swift
  92. @BriceDutheil @dwursteisen #DevoxxFR #Rx Historique

  93. @BriceDutheil @dwursteisen #DevoxxFR #Rx Pourquoi faire ? Une API pour

    la programmation asynchrone
  94. @BriceDutheil @dwursteisen #DevoxxFR #Rx Appel Synchrone - Asynchrone Appel synchrone

    versus Appel asynchrone
  95. @BriceDutheil @dwursteisen #DevoxxFR #Rx Appel Synchrone Client Serveur

  96. @BriceDutheil @dwursteisen #DevoxxFR #Rx Appel Synchrone Client Serveur Appel bloquant

  97. @BriceDutheil @dwursteisen #DevoxxFR #Rx Appel Asynchrone Client Serveur

  98. @BriceDutheil @dwursteisen #DevoxxFR #Rx Appel Asynchrone Exécution d’autres traitements Client

    Serveur
  99. @BriceDutheil @dwursteisen #DevoxxFR #Rx Temporalité http://blog.codinghorror.com/the-infinite-space-between-words/ Temps machine Temps humain

    1 cycle CPU 0.3 nanosecondes 1 seconde Accès disque (SSD) 50-150 microsecondes 2-6 jours Accès disque (mécanique) 1-10 millisecondes 1-12 mois Envoi d’un paquet de San Francisco à New York 40 milliseconds 4 ans
  100. @BriceDutheil @dwursteisen #DevoxxFR #Rx Temporalité http://blog.codinghorror.com/the-infinite-space-between-words/ Temps machine Temps humain

    1 cycle CPU 0.3 nanosecondes 1 seconde Accès disque (SSD) 50-150 microsecondes 2-6 jours Accès disque (mécanique) 1-10 millisecondes 1-12 mois Envoi d’un paquet de San Francisco à New York 40 milliseconds 4 ans
  101. @BriceDutheil @dwursteisen #DevoxxFR #Rx Pour quoi faire ?

  102. @BriceDutheil @dwursteisen #DevoxxFR #Rx Pour quoi faire ? Future ?

    Callback ?
  103. @BriceDutheil @dwursteisen #DevoxxFR #Rx Pour quoi faire ? Future<String> future

    = … future.get(); // bloquant !
  104. @BriceDutheil @dwursteisen #DevoxxFR #Rx Pour quoi faire ? future1.get(); future2.get();

    future3.get(); future4.get(); future5.get(); Ordonnancement optimal ?
  105. @BriceDutheil @dwursteisen #DevoxxFR #Rx Pour quoi faire ? Future ?

    Callback ?
  106. @BriceDutheil @dwursteisen #DevoxxFR #Rx Pour quoi faire ? client.execute(new Callback()

    { @Override public void completed(HttpResponse response) { } });
  107. @BriceDutheil @dwursteisen #DevoxxFR #Rx Pour quoi faire ? client.execute(new Callback()

    { @Override public void completed(HttpResponse response) { client.execute(new Callback() { @Override public void completed(HttpResponse response) { } }); } });
  108. @BriceDutheil @dwursteisen #DevoxxFR #Rx Pour quoi faire ? client.execute(new Callback()

    { @Override public void completed(HttpResponse response) { client.execute(new Callback() { @Override public void completed(HttpResponse response) { client.execute(new Callback() { @Override public void completed(HttpResponse response) { } }); } });
  109. @BriceDutheil @dwursteisen #DevoxxFR #Rx Pour quoi faire ? client.execute(new Callback()

    { @Override public void completed(HttpResponse response) { client.execute(new Callback() { @Override public void completed(HttpResponse response) { client.execute(new Callback() { @Override public void completed(HttpResponse response) { } }); } }); Callback hell
  110. @BriceDutheil @dwursteisen #DevoxxFR #Rx Pour quoi faire ? Future ?

    Callback ?
  111. @BriceDutheil @dwursteisen #DevoxxFR #Rx Comprendre les concepts Les technologies passent.

    Les concepts restent.
  112. @BriceDutheil @dwursteisen #DevoxxFR #Rx Concepts applicables pour d’autres bibliothèques de

    type Reactive Streams
  113. @BriceDutheil @dwursteisen #DevoxxFR #Rx Back to basics

  114. @BriceDutheil @dwursteisen #DevoxxFR #Rx RxJava permet de manipuler des évènements

    d’une manière synchrone et/ou asynchrone
  115. @BriceDutheil @dwursteisen #DevoxxFR #Rx Flux d’évènements fini Diagramme Marble

  116. @BriceDutheil @dwursteisen #DevoxxFR #Rx Flux d’évènements fini Évènements Fin du

    flux
  117. @BriceDutheil @dwursteisen #DevoxxFR #Rx Flux d’évènements avec une erreur Évènements

    Erreur
  118. @BriceDutheil @dwursteisen #DevoxxFR #Rx Flux d’évènements infini Évènements

  119. @BriceDutheil @dwursteisen #DevoxxFR #Rx Flux fini sans évènement Fin du

    flux
  120. @BriceDutheil @dwursteisen #DevoxxFR #Rx Quelle est la nature de ces

    évènements ?
  121. @BriceDutheil @dwursteisen #DevoxxFR #Rx json json json Observable<json>

  122. @BriceDutheil @dwursteisen #DevoxxFR #Rx λ ϕ φ json json json

    Observable<json> Observable<Integer>
  123. @BriceDutheil @dwursteisen #DevoxxFR #Rx λ ϕ φ json json json

    Observable<json> Observable<Integer> Observable<Click>
  124. @BriceDutheil @dwursteisen #DevoxxFR #Rx Comment écouter ces évènements ?

  125. @BriceDutheil @dwursteisen #DevoxxFR #Rx Observer

  126. @BriceDutheil @dwursteisen #DevoxxFR #Rx OnNext* (OnCompleted|OnError)?

  127. @BriceDutheil @dwursteisen #DevoxxFR #Rx OnNext* (OnCompleted|OnError)?

  128. @BriceDutheil @dwursteisen #DevoxxFR #Rx OnNext* (OnCompleted|OnError)?

  129. @BriceDutheil @dwursteisen #DevoxxFR #Rx OnNext* (OnCompleted|OnError)?

  130. @BriceDutheil @dwursteisen #DevoxxFR #Rx Observable.just(1, 2, 3) .subscribe(new Observer<Integer>() {

    @Override public void onNext(Integer v) { System.out.println("Valeur : " + v); } @Override public void onCompleted() { System.out.println("Fin"); } @Override public void onError(Throwable e) { System.out.println("Erreur"); } });
  131. @BriceDutheil @dwursteisen #DevoxxFR #Rx Observable.just(1, 2, 3) .subscribe(new Observer<Integer>() {

    @Override public void onNext(Integer v) { System.out.println("Valeur : " + v); } @Override public void onCompleted() { System.out.println("Fin"); } @Override public void onError(Throwable e) { System.out.println("Erreur"); } });
  132. @BriceDutheil @dwursteisen #DevoxxFR #Rx Observable.just(1, 2, 3) .subscribe(new Observer<Integer>() {

    @Override public void onNext(Integer v) { System.out.println("Valeur : " + v); } @Override public void onCompleted() { System.out.println("Fin"); } @Override public void onError(Throwable e) { System.out.println("Erreur"); } });
  133. @BriceDutheil @dwursteisen #DevoxxFR #Rx Observable.just(1, 2, 3) .subscribe(new Observer<Integer>() {

    @Override public void onNext(Integer v) { System.out.println("Valeur : " + v); } @Override public void onCompleted() { System.out.println("Fin"); } @Override public void onError(Throwable e) { System.out.println("Erreur"); } });
  134. @BriceDutheil @dwursteisen #DevoxxFR #Rx Observable.just(1, 2, 3) .subscribe(v-> System.out.println("Valeur :

    " + v), e -> System.out.println("Erreur"), () -> System.out.println("Fin") );
  135. @BriceDutheil @dwursteisen #DevoxxFR #Rx Observable.just(1, 2, 3) .subscribe(v-> System.out.println("Valeur :

    " + v), e -> System.out.println("Erreur"), () -> System.out.println("Fin") ); onNext onError onCompleted
  136. @BriceDutheil @dwursteisen #DevoxxFR #Rx Comment manipuler ces évènements ?

  137. @BriceDutheil @dwursteisen #DevoxxFR #Rx Observable

  138. @BriceDutheil @dwursteisen #DevoxxFR #Rx Transformation à base d’opérateurs.

  139. @BriceDutheil @dwursteisen #DevoxxFR #Rx Observable.just(1, 2, 3) .map(i -> i

    * 2) .take(2) .defaultIfEmpty(0) .subscribe();
  140. @BriceDutheil @dwursteisen #DevoxxFR #Rx Observable.just(1, 2, 3) .map(i -> i

    * 2) .take(2) .defaultIfEmpty(0) .subscribe(); Opérateur map
  141. @BriceDutheil @dwursteisen #DevoxxFR #Rx Observable.just(1, 2, 3) .map(i -> i

    * 2) .take(2) .defaultIfEmpty(0) .subscribe(); Opérateur take
  142. @BriceDutheil @dwursteisen #DevoxxFR #Rx Observable.just(1, 2, 3) .map(i -> i

    * 2) .take(2) .defaultIfEmpty(0) .subscribe(); Opérateur defaultIfEmpty
  143. @BriceDutheil @dwursteisen #DevoxxFR #Rx Liste des opérateurs disponibles sur reactivex.io

  144. @BriceDutheil @dwursteisen #DevoxxFR #Rx Question : Comment convertir une liste

    de mots en minuscules et filtrer pour garder uniquement les mots de plus de 3 lettres ?
  145. @BriceDutheil @dwursteisen #DevoxxFR #Rx Observable.just("bjr", "DevoXx") .map(str -> str.toLowerCase()) .filter(str

    -> str.length() > 3) .subscribe(System.out::println);
  146. @BriceDutheil @dwursteisen #DevoxxFR #Rx Observable.just("bjr", "DevoXx") .map(str -> str.toLowerCase()) .filter(str

    -> str.length() > 3) .subscribe(System.out::println); Conversion
  147. @BriceDutheil @dwursteisen #DevoxxFR #Rx Observable.just("bjr", "DevoXx") .map(str -> str.toLowerCase()) .filter(str

    -> str.length() > 3) .subscribe(System.out::println); Filtre
  148. @BriceDutheil @dwursteisen #DevoxxFR #Rx Observable.just("bjr", "DevoXx") .map(str -> str.toLowerCase()) .filter(str

    -> str.length() > 3) .subscribe(System.out::println); Observable 1
  149. @BriceDutheil @dwursteisen #DevoxxFR #Rx Observable.just("bjr", "DevoXx") .map(str -> str.toLowerCase()) .filter(str

    -> str.length() > 3) .subscribe(System.out::println); Observable 2
  150. @BriceDutheil @dwursteisen #DevoxxFR #Rx Observable.just("bjr", "DevoXx") .map(str -> str.toLowerCase()) .filter(str

    -> str.length() > 3) .subscribe(System.out::println); Observable 3
  151. @BriceDutheil @dwursteisen #DevoxxFR #Rx Comprendre les concepts Manipuler une monade

  152. @BriceDutheil @dwursteisen #DevoxxFR #Rx Comprendre les concepts

  153. @BriceDutheil @dwursteisen #DevoxxFR #Rx Concept de Monade Une structure ayant

    - Un type paramétré (Observable<T>) - Une factory (Observable.just(...)) - Une méthode flatMap (obs.flatMap(...)) - Structure programmatique (Grossièrement)
  154. @BriceDutheil @dwursteisen #DevoxxFR #Rx Concept de Monade Stream Java 8

  155. @BriceDutheil @dwursteisen #DevoxxFR #Rx Structure programmatique List<String> words = Stream.of("bonJour",

    "bonjour", "Hello") .map(str -> str.toLowerCase()) .distinct() .collect(Collectors.toList()); Factory
  156. @BriceDutheil @dwursteisen #DevoxxFR #Rx Structure programmatique List<String> words = Stream.of("bonJour",

    "bonjour", "Hello") .map(str -> str.toLowerCase()) .distinct() .collect(Collectors.toList()); Étape 1
  157. @BriceDutheil @dwursteisen #DevoxxFR #Rx Structure programmatique List<String> words = Stream.of("bonJour",

    "bonjour", "Hello") .map(str -> str.toLowerCase()) .distinct() .collect(Collectors.toList()); Étape 2
  158. @BriceDutheil @dwursteisen #DevoxxFR #Rx Structure programmatique List<String> words = Stream.of("bonJour",

    "bonjour", "Hello") .map(str -> str.toLowerCase()) .distinct() .collect(Collectors.toList()); Étape 3 (fonction terminale)
  159. @BriceDutheil @dwursteisen #DevoxxFR #Rx Concept de Monade Observable RxJava

  160. @BriceDutheil @dwursteisen #DevoxxFR #Rx Structure programmatique Observable.just("bonJour", "bonjour", "Hello") .map(str

    -> str.toLowerCase()) .distinct() .subscribe(); Factory
  161. @BriceDutheil @dwursteisen #DevoxxFR #Rx Structure programmatique Observable.just("bonJour", "bonjour", "Hello") .map(str

    -> str.toLowerCase()) .distinct() .subscribe(); Étape 1
  162. @BriceDutheil @dwursteisen #DevoxxFR #Rx Structure programmatique Observable.just("bonJour", "bonjour", "Hello") .map(str

    -> str.toLowerCase()) .distinct() .subscribe(); Étape 2
  163. @BriceDutheil @dwursteisen #DevoxxFR #Rx Structure programmatique Observable.just("bonJour", "bonjour", "Hello") .map(str

    -> str.toLowerCase()) .distinct() .subscribe(); Étape 3 (fonction terminale)
  164. @BriceDutheil @dwursteisen #DevoxxFR #Rx Avec les Reactive Extensions, nous allons

    principalement manipuler des Observables
  165. @BriceDutheil @dwursteisen #DevoxxFR #Rx Structure programmatique Observable = Monade

  166. @BriceDutheil @dwursteisen #DevoxxFR #Rx Structure programmatique Observable = Programmation fonctionnelle

  167. @BriceDutheil @dwursteisen #DevoxxFR #Rx Évitez les effets de bord Structure

    programmatique une fonction est dite à effet de bord si elle modifie un état autre que sa valeur de retour
  168. @BriceDutheil @dwursteisen #DevoxxFR #Rx Évitez les effets de bord Structure

    programmatique une fonction est dite à effet de bord si elle modifie un état autre que sa valeur de retour (Comme avec les streams Java 8)
  169. @BriceDutheil @dwursteisen #DevoxxFR #Rx Question : Comment créer une liste

    à partir d’un ensemble de valeurs ?
  170. @BriceDutheil @dwursteisen #DevoxxFR #Rx Effets de bord List<Integer> result =

    new ArrayList<>(); Observable.just(1, 2, 3) .map(i -> result.add(i)) .subscribe();
  171. @BriceDutheil @dwursteisen #DevoxxFR #Rx List<Integer> result = new ArrayList<>(); Observable.just(1,

    2, 3) .map(i -> result.add(i)) .subscribe(); Effets de bord Effet de bord
  172. @BriceDutheil @dwursteisen #DevoxxFR #Rx Observable.just(1, 2, 3) .toList() .subscribe(result ->

    {/* ... */}); Effets de bord Pas d’effet de bord
  173. @BriceDutheil @dwursteisen #DevoxxFR #Rx Observable.just(1, 2, 3) .toList() .subscribe(result ->

    {/* ... */}); Effets de bord Consommation de la liste
  174. @BriceDutheil @dwursteisen #DevoxxFR #Rx Question : Comment logger une opération

    ?
  175. @BriceDutheil @dwursteisen #DevoxxFR #Rx Effets de bord Observable.just(1, 2, 3)

    .map(i -> { LOGGER.info("value : " + i); return i; }) .subscribe();
  176. @BriceDutheil @dwursteisen #DevoxxFR #Rx Observable.just(1, 2, 3) .map(i -> {

    LOGGER.info("value : " + i); return i; }) .subscribe(); Effets de bord Effet de bord
  177. @BriceDutheil @dwursteisen #DevoxxFR #Rx Évitez les effets de bord (ou

    soyez explicite)
  178. @BriceDutheil @dwursteisen #DevoxxFR #Rx Effets de bord Observable.just(1, 2, 3)

    // explicite .doOnNext(i -> LOGGER.info("Valeur :" + i)) .subscribe();
  179. @BriceDutheil @dwursteisen #DevoxxFR #Rx Effets de bord Observable.just(1, 2, 3)

    .doOnSubscribe(() -> LOGGER.info("Subscribe")) .doOnNext(i -> LOGGER.info("Valeur :" + i)) .doOnTerminate(() -> LOGGER.info("Terminate")) .subscribe();
  180. @BriceDutheil @dwursteisen #DevoxxFR #Rx Effets de bord Observable.just(1, 2, 3)

    .doOnSubscribe(() -> LOGGER.info("Subscribe")) .doOnNext(i -> LOGGER.info("Valeur :" + i)) .doOnTerminate(() -> LOGGER.info("Terminate")) .subscribe();
  181. @BriceDutheil @dwursteisen #DevoxxFR #Rx Effets de bord Observable.just(1, 2, 3)

    .doOnSubscribe(() -> LOGGER.info("Subscribe")) .doOnNext(i -> LOGGER.info("Valeur :" + i)) .doOnTerminate(() -> LOGGER.info("Terminate")) .subscribe();
  182. @BriceDutheil @dwursteisen #DevoxxFR #Rx Effets de bord Observable.just(1, 2, 3)

    .doOnSubscribe(() -> LOGGER.info("Subscribe")) .doOnNext(i -> LOGGER.info("Valeur :" + i)) .doOnTerminate(() -> LOGGER.info("Terminate")) .subscribe();
  183. @BriceDutheil @dwursteisen #DevoxxFR #Rx Question : Comment compter le nombre

    d’erreur de différents Observables ?
  184. @BriceDutheil @dwursteisen #DevoxxFR #Rx // attention ! état partagé !

    AtomicLong errors = new AtomicLong(0); Observable.just(1, 2, 3) .doOnError(ex -> errors.incrementAndGet()) .subscribe();
  185. @BriceDutheil @dwursteisen #DevoxxFR #Rx // attention ! état partagé !

    AtomicLong errors = new AtomicLong(0); Observable.just(1, 2, 3) .doOnError(ex -> errors.incrementAndGet()) .subscribe();
  186. @BriceDutheil @dwursteisen #DevoxxFR #Rx Question : Comment créer des Observables

    ?
  187. @BriceDutheil @dwursteisen #DevoxxFR #Rx Comment créer des Observables Observable.just() Observable.fromCallable()

    Observable.defer() Observable.using() Observable.create() Complexité
  188. @BriceDutheil @dwursteisen #DevoxxFR #Rx Comment créer des Observables Observable.just() Observable.fromCallable()

    Observable.defer() Observable.using() Observable.create() Simple d’utilisation
  189. @BriceDutheil @dwursteisen #DevoxxFR #Rx Comment créer des Observables Observable.just() Observable.fromCallable()

    Observable.defer() Observable.using() Observable.create() Complexe d’utilisation
  190. @BriceDutheil @dwursteisen #DevoxxFR #Rx Comment créer des Observables Observable.just() Observable.fromCallable()

    Observable.defer() Observable.using() Observable.create() Complexe d’utilisation Offre un maximum de contrôle
  191. @BriceDutheil @dwursteisen #DevoxxFR #Rx Quelle méthode pour quelle utilisation ?

  192. @BriceDutheil @dwursteisen #DevoxxFR #Rx Observable.just(compute()) .subscribe();

  193. @BriceDutheil @dwursteisen #DevoxxFR #Rx Observable.just(compute()) .subscribe();

  194. @BriceDutheil @dwursteisen #DevoxxFR #Rx Observable.just(compute()) .subscribe(); Appelé immédiatement

  195. @BriceDutheil @dwursteisen #DevoxxFR #Rx int result = compute(); Observable.just(result) .subscribe();

  196. @BriceDutheil @dwursteisen #DevoxxFR #Rx int result = compute(); Observable.just(result) .subscribe();

  197. @BriceDutheil @dwursteisen #DevoxxFR #Rx Observable.just permet d'émettre une valeur déjà

    existante.
  198. @BriceDutheil @dwursteisen #DevoxxFR #Rx Observable.defer(() -> Observable.just(compute())) .subscribe();

  199. @BriceDutheil @dwursteisen #DevoxxFR #Rx Observable.defer(() -> Observable.just(compute())) .subscribe();

  200. @BriceDutheil @dwursteisen #DevoxxFR #Rx Observable.defer(() -> Observable.just(compute())) .subscribe(); Création tardive

  201. @BriceDutheil @dwursteisen #DevoxxFR #Rx Observable.defer(() -> Observable.just(compute())) .subscribe(); Création tardive

    Création d’un Observable ?
  202. @BriceDutheil @dwursteisen #DevoxxFR #Rx Observable.defer permet de créer tardivement un

    Observable.
  203. @BriceDutheil @dwursteisen #DevoxxFR #Rx Observable.fromCallable(() -> compute()) .subscribe();

  204. @BriceDutheil @dwursteisen #DevoxxFR #Rx Observable.fromCallable(() -> compute()) .subscribe(); Création tardive

  205. @BriceDutheil @dwursteisen #DevoxxFR #Rx Observable.fromCallable permet d'émettre tardivement une valeur.

  206. @BriceDutheil @dwursteisen #DevoxxFR #Rx Observable.using( () -> new DB(), db

    -> Observable.just(db.doQuery()), db -> db.closeDb()) .subscribe();
  207. @BriceDutheil @dwursteisen #DevoxxFR #Rx Observable.using( () -> new DB(), db

    -> Observable.just(db.doQuery()), db -> db.closeDb()) .subscribe(); Création d’une ressource
  208. @BriceDutheil @dwursteisen #DevoxxFR #Rx Observable.using( () -> new DB(), db

    -> Observable.just(db.doQuery()), db -> db.closeDb()) .subscribe(); Émission d’un résultat
  209. @BriceDutheil @dwursteisen #DevoxxFR #Rx Observable.using( () -> new DB(), db

    -> Observable.just(db.doQuery()), db -> db.closeDb()) .subscribe(); Fermeture de la ressource
  210. @BriceDutheil @dwursteisen #DevoxxFR #Rx Observable.using permet d'associer un Observable à

    une ressource.
  211. @BriceDutheil @dwursteisen #DevoxxFR #Rx Attention Zone Dangereuse !

  212. @BriceDutheil @dwursteisen #DevoxxFR #Rx Observable.create(subscriber -> { subscriber.add(/* ... */);

    subscriber.setProducer(n -> { subscriber.onNext(/* ... */); // ... subscriber.onCompleted(); }); }).subscribe();
  213. @BriceDutheil @dwursteisen #DevoxxFR #Rx Observable.create(subscriber -> { subscriber.add(/* ... */);

    subscriber.setProducer(n -> { subscriber.onNext(/* ... */); // ... subscriber.onCompleted(); }); }).subscribe(); ? ? ?
  214. @BriceDutheil @dwursteisen #DevoxxFR #Rx Observable.create(subscriber -> { subscriber.add(/* ... */);

    subscriber.setProducer(n -> { subscriber.onNext(/* ... */); // ... subscriber.onCompleted(); }); }).subscribe(); Désabonnement Backpressure Contrat Rx
  215. @BriceDutheil @dwursteisen #DevoxxFR #Rx Observable.create(subscriber -> { subscriber.onNext(compute()); subscriber.onCompleted(); }).subscribe();

  216. @BriceDutheil @dwursteisen #DevoxxFR #Rx Observable.create(subscriber -> { subscriber.onNext(compute()); subscriber.onCompleted(); }).subscribe();

    Contrat Rx
  217. @BriceDutheil @dwursteisen #DevoxxFR #Rx Observable.fromCallable(() -> compute()) .subscribe(); Plus simple

    !
  218. @BriceDutheil @dwursteisen #DevoxxFR #Rx Observable.create(subscriber -> { for (int i

    = 0; i < 10; i++) { if (!subscriber.isUnsubscribed()) { subscriber.onNext(compute(i)); } } subscriber.onCompleted(); }).subscribe();
  219. @BriceDutheil @dwursteisen #DevoxxFR #Rx Observable.create(subscriber -> { for (int i

    = 0; i < 10; i++) { if (!subscriber.isUnsubscribed()) { subscriber.onNext(compute(i)); } } subscriber.onCompleted(); }).subscribe(); Le flux est-il encore actif ? Éviter de lancer ce calcul (coûteux)
  220. @BriceDutheil @dwursteisen #DevoxxFR #Rx Observable.range(1, 10) .map(i -> compute(i)) .subscribe(System.out::println);

    Plus simple !
  221. @BriceDutheil @dwursteisen #DevoxxFR #Rx Observable.create(subscriber -> { DB db =

    new DB(); subscriber.add(Subscriptions.create(() -> db.closeDb())); subscriber.onNext(db); subscriber.onCompleted(); }).subscribe();
  222. @BriceDutheil @dwursteisen #DevoxxFR #Rx Observable.create(subscriber -> { DB db =

    new DB(); subscriber.add(Subscriptions.create(() -> db.closeDb())); subscriber.onNext(db); subscriber.onCompleted(); }).subscribe(); Code de désabonnement
  223. @BriceDutheil @dwursteisen #DevoxxFR #Rx Observable.using( () -> new DB(), db

    -> Observable.just(db.doQuery()), db -> db.closeDb()) .subscribe(); Plus simple !
  224. @BriceDutheil @dwursteisen #DevoxxFR #Rx Observable.create permet de gérer le contrat

    Rx et le désabonnement et la backpressure
  225. @BriceDutheil @dwursteisen #DevoxxFR #Rx Observable.create vous oblige à gérer le

    contrat Rx et le désabonnement et la backpressure
  226. #DevoxxFR #Rx Reactive Extensions En pratique

  227. @BriceDutheil @dwursteisen #DevoxxFR #Rx Connaissez vous d’autres modèles de concurrence

    ?
  228. @BriceDutheil @dwursteisen #DevoxxFR #Rx Connaissez vous d’autres modèles de concurrence

    ? Non Thread uniquement Oui Acteur, STM, Channels …
  229. @BriceDutheil @dwursteisen #DevoxxFR #Rx Connaissez vous vous d’autres modèles de

    concurrence ? Non Thread uniquement
  230. @BriceDutheil @dwursteisen #DevoxxFR #Rx Connaissez vous vous d’autres modèles de

    concurrence ? Oui Acteur, STM, Channels …
  231. @BriceDutheil @dwursteisen #DevoxxFR #Rx Pour explorer d’autres modèles de concurrence

  232. @BriceDutheil @dwursteisen #DevoxxFR #Rx Modèle de concurrence

  233. @BriceDutheil @dwursteisen #DevoxxFR #Rx On a vu Tomcat et les

    servlets OS Tomcat / BIO client Requête # Acceptor Thread Thread war
  234. @BriceDutheil @dwursteisen #DevoxxFR #Rx Modèle de concurrence de RxJava

  235. @BriceDutheil @dwursteisen #DevoxxFR #Rx method() { Observable.from(items) .map(this::transform) .flatMap(this::computeRelativeItems) .subscribe(response::resume,

    response::resume); }
  236. @BriceDutheil @dwursteisen #DevoxxFR #Rx method() { Observable.from(items) .map(this::transform) .flatMap(this::computeRelativeItems) .subscribe(response::resume,

    response::resume); } Travail sur le Thread courant
  237. @BriceDutheil @dwursteisen #DevoxxFR #Rx Par défaut synchrone* method() { Observable.from(items)

    .map(this::transform) .flatMap(this::computeRelativeItems) .subscribe(response::resume, response::resume); } Travail sur le Thread courant
  238. @BriceDutheil @dwursteisen #DevoxxFR #Rx RxJava permet un contrôle explicite de

    la concurrence avec observeOn() ou avec subscribeOn()
  239. @BriceDutheil @dwursteisen #DevoxxFR #Rx method() { Observable.from(items) .map(this::transform) .flatMap(this::computeRelativeItems) .subscribe(response::resume,

    response::resume); } Thread courant
  240. @BriceDutheil @dwursteisen #DevoxxFR #Rx method() { Observable.from(items) .observeOn( ) .map(this::transform)

    .flatMap(this::computeRelativeItems) .subscribe(response::resume, response::resume); } Thread courant
  241. @BriceDutheil @dwursteisen #DevoxxFR #Rx method() { Observable.from(items) .observeOn( ) .map(this::transform)

    .flatMap(this::computeRelativeItems) .subscribe(response::resume, response::resume); } Thread courant
  242. @BriceDutheil @dwursteisen #DevoxxFR #Rx method() { Observable.from(items) .observeOn(Schedulers.computation()) .map(this::transform) .flatMap(this::computeRelativeItems)

    .subscribe(response::resume, response::resume); } Thread courant Travail planifié sur computation
  243. @BriceDutheil @dwursteisen #DevoxxFR #Rx observeOn() change le contexte des opérations

    qui suivent
  244. @BriceDutheil @dwursteisen #DevoxxFR #Rx Il peut y avoir plusieurs observeOn()

  245. @BriceDutheil @dwursteisen #DevoxxFR #Rx Par défaut synchrone* method() { Observable.from(items)

    .map(this::transform) .delay(10, SECONDS)) .subscribe(response::resume, response::resume); } Travail sur le Thread courant * Certains opérateurs fonctionnent par défaut sur un autre Scheduler
  246. @BriceDutheil @dwursteisen #DevoxxFR #Rx Par défaut synchrone* method() { Observable.from(items)

    .map(this::transform) .delay(10, SECONDS)) .subscribe(response::resume, response::resume); } Travail sur le Thread courant * Certains opérateurs fonctionnent par défaut sur un autre Scheduler Change le contexte
  247. @BriceDutheil @dwursteisen #DevoxxFR #Rx Certains opérateurs ont le même effet

    que observeOn()
  248. @BriceDutheil @dwursteisen #DevoxxFR #Rx method() { Observable.from(items) .observeOn(Schedulers.computation()) .map(this::transform) .flatMap(this::computeRelativeItems)

    .subscribe(response::resume, response::resume); } Thread courant Travail planifié sur computation
  249. @BriceDutheil @dwursteisen #DevoxxFR #Rx method() { Observable.from(items) .observeOn(Schedulers.computation()) .map(this::transform) .flatMap(this::computeRelativeItems)

    .subscribeOn( ) .subscribe(response::resume, response::resume); } Thread courant Travail planifié sur computation
  250. @BriceDutheil @dwursteisen #DevoxxFR #Rx method() { Observable.from(items) .observeOn(Schedulers.computation()) .map(this::transform) .flatMap(this::computeRelativeItems)

    .subscribeOn( ) .subscribe(response::resume, response::resume); } Thread courant Travail planifié sur computation
  251. @BriceDutheil @dwursteisen #DevoxxFR #Rx method() { Observable.from(items) .observeOn(Schedulers.computation()) .map(this::transform) .flatMap(this::computeRelativeItems)

    .subscribeOn(Schedulers.io()) .subscribe(response::resume, response::resume); } Thread courant Travail planifié sur computation Production des éléments sur io
  252. @BriceDutheil @dwursteisen #DevoxxFR #Rx subscribeOn() définit le contexte où s’exécute

    la production des éléments
  253. @BriceDutheil @dwursteisen #DevoxxFR #Rx Il ne peut y avoir qu’un

    seul subscribeOn() Premier arrivé, premier servi !
  254. @BriceDutheil @dwursteisen #DevoxxFR #Rx 2 principaux modèles de schedulers

  255. @BriceDutheil @dwursteisen #DevoxxFR #Rx computation() .map(str -> …) Élément #

    .filter(…) Élément <> .merge(…) Élément ‘a’ .map(str -> …)
  256. @BriceDutheil @dwursteisen #DevoxxFR #Rx computation() Nombre de coeur CPU .map(str

    -> …) Élément # .filter(…) Élément <> .merge(…) Élément ‘a’ .map(str -> …)
  257. @BriceDutheil @dwursteisen #DevoxxFR #Rx computation() Nombre de coeur CPU .debounce(…)

    .filter(e -> ...) .merge(...) Élément # .filter(…) Élément <> .merge(…)
  258. @BriceDutheil @dwursteisen #DevoxxFR #Rx Le modèle de computation() est une

    boucle d’évènements
  259. @BriceDutheil @dwursteisen #DevoxxFR #Rx Augmente le rendement du CPU

  260. @BriceDutheil @dwursteisen #DevoxxFR #Rx Thread.sleep() CountDownLatch.await() for(;;) { /* do

    work */ } I/O
  261. @BriceDutheil @dwursteisen #DevoxxFR #Rx Thread.sleep() CountDownLatch.await() for(;;) { /* do

    work */ } I/O NON
  262. @BriceDutheil @dwursteisen #DevoxxFR #Rx Ne PAS bloquer la boucle d’évènements

  263. @BriceDutheil @dwursteisen #DevoxxFR #Rx io() Élément # .filter(…) Élément <>

    .merge(…) Élément ‘a’ .flatMap(…)
  264. @BriceDutheil @dwursteisen #DevoxxFR #Rx io() DB Élément # .filter(…) Élément

    <> .merge(…) Élément ‘a’ .flatMap(…)
  265. @BriceDutheil @dwursteisen #DevoxxFR #Rx io() DB Élément # .filter(…) Élément

    <> .merge(…) Élément ‘a’ .flatMap(…)
  266. @BriceDutheil @dwursteisen #DevoxxFR #Rx io() DB Network Internet Élément #

    .filter(…) Élément <> .merge(…) Élément ‘a’ .flatMap(…)
  267. @BriceDutheil @dwursteisen #DevoxxFR #Rx io() DB Network Internet Élément #

    .filter(…) Élément <> .merge(…) Élément ‘a’ .flatMap(…)
  268. @BriceDutheil @dwursteisen #DevoxxFR #Rx io() DB Network Internet Élément #

    .filter(…) Élément <> .merge(…) Élément ‘a’ .flatMap(…)
  269. @BriceDutheil @dwursteisen #DevoxxFR #Rx Le modèle du scheduler io() est

    un pool de thread qui grossit au besoin
  270. @BriceDutheil @dwursteisen #DevoxxFR #Rx io() ne bloque pas le CPU

  271. @BriceDutheil @dwursteisen #DevoxxFR #Rx Avec JAX-RS

  272. @BriceDutheil @dwursteisen #DevoxxFR #Rx @Path("heavy-stuff") public static class WaitResource {

    @GET @Path("{heavyness}") public Response wait(@PathParam("heavyness") int heavyness) throws InterruptedException { System.out.format("heavy work that will take %dms%n", heavyness); MILLISECONDS.sleep(heavyness); // hard and long work return Response.ok(format("Waited %dms", heavyness)).build(); } }
  273. @BriceDutheil @dwursteisen #DevoxxFR #Rx @Path("heavy-stuff") public static class WaitResource {

    @GET @Path("{heavyness}") public Response wait(@PathParam("heavyness") int heavyness) throws InterruptedException { System.out.format("heavy work that will take %dms%n", heavyness); MILLISECONDS.sleep(heavyness); // hard and long work return Response.ok(format("Waited %dms", heavyness)).build(); } } Travail
  274. @BriceDutheil @dwursteisen #DevoxxFR #Rx @Path("heavy-stuff") public static class WaitResource {

    @GET @Path("{heavyness}") public void wait(@Suspended AsyncResponse ar, @PathParam("heavyness") int heavyness) { executor.submit(() -> { try { MILLISECONDS.sleep(heavyness); ar.resume(format("Waited %dms", heavyness)); } catch (InterruptedException e) { Thread.interrupted(); ar.resume(e); } }); } }
  275. @BriceDutheil @dwursteisen #DevoxxFR #Rx @Path("heavy-stuff") public static class WaitResource {

    @GET @Path("{heavyness}") public void wait(@Suspended AsyncResponse ar, @PathParam("heavyness") int heavyness) { executor.submit(() -> { try { MILLISECONDS.sleep(heavyness); ar.resume(format("Waited %dms", heavyness)); } catch (InterruptedException e) { Thread.interrupted(); ar.resume(e); } }); } } JAX-RS 2.0 (Servlet 3.0)
  276. @BriceDutheil @dwursteisen #DevoxxFR #Rx @Path("heavy-stuff") public static class WaitResource {

    @GET @Path("{heavyness}") public void wait(@Suspended AsyncResponse ar, @PathParam("heavyness") int heavyness) { executor.submit(() -> { try { MILLISECONDS.sleep(heavyness); ar.resume(format("Waited %dms", heavyness)); } catch (InterruptedException e) { Thread.interrupted(); ar.resume(e); } }); } } JAX-RS 2.0 (Servlet 3.0) Tache asynchrone
  277. @BriceDutheil @dwursteisen #DevoxxFR #Rx @Path("heavy-stuff") public static class WaitResource {

    @GET @Path("{heavyness}") public void wait(@Suspended AsyncResponse ar, @PathParam("heavyness") int heavyness) { executor.submit(() -> { try { MILLISECONDS.sleep(heavyness); ar.resume(format("Waited %dms", heavyness)); } catch (InterruptedException e) { Thread.interrupted(); ar.resume(e); } }); } } JAX-RS 2.0 (Servlet 3.0) thread pool Tache asynchrone
  278. @BriceDutheil @dwursteisen #DevoxxFR #Rx @Path("heavy-stuff") public static class WaitResource {

    @GET @Path("{heavyness}") public void wait(@Suspended AsyncResponse ar, @PathParam("heavyness") int heavyness) { executor.submit(() -> { try { MILLISECONDS.sleep(heavyness); ar.resume(format("Waited %dms", heavyness)); } catch (InterruptedException e) { Thread.interrupted(); ar.resume(e); } }); } } JAX-RS 2.0 (Servlet 3.0) thread pool Tache asynchrone response.resume() pour retourner la réponse HTTP
  279. @BriceDutheil @dwursteisen #DevoxxFR #Rx JAX-RS et Rx

  280. @BriceDutheil @dwursteisen #DevoxxFR #Rx @Path("heavy-stuff") public static class WaitResource {

    @GET @Path("{heavyness}") public void wait(@Suspended AsyncResponse ar, @PathParam("heavyness") int heavyness) { response.setTimeout(50, MILLISECONDS); Observable.just(waitTime) .observeOn(Schedulers.computation()) .map(this::longProcess) .subscribe(result -> ar.resume(result), throwable -> ar.resume(throwable)); } private String longProcess(int heavyness) { ... } }
  281. @BriceDutheil @dwursteisen #DevoxxFR #Rx @Path("heavy-stuff") public static class WaitResource {

    @GET @Path("{heavyness}") public void wait(@Suspended AsyncResponse ar, @PathParam("heavyness") int heavyness) { response.setTimeout(50, MILLISECONDS); Observable.just(waitTime) .observeOn(Schedulers.computation()) .map(this::longProcess) .subscribe(result -> ar.resume(result), throwable -> ar.resume(throwable)); } private String longProcess(int heavyness) { ... } } Traitement asynchrone avec Rx
  282. @BriceDutheil @dwursteisen #DevoxxFR #Rx @Path("heavy-stuff") public static class WaitResource {

    @GET @Path("{heavyness}") public void wait(@Suspended AsyncResponse ar, @PathParam("heavyness") int heavyness) { response.setTimeout(50, MILLISECONDS); Observable.just(waitTime) .observeOn(Schedulers.computation()) .map(this::longProcess) .subscribe(result -> ar.resume(result), throwable -> ar.resume(throwable)); } private String longProcess(int heavyness) { ... } } Prend les paramètres
  283. @BriceDutheil @dwursteisen #DevoxxFR #Rx @Path("heavy-stuff") public static class WaitResource {

    @GET @Path("{heavyness}") public void wait(@Suspended AsyncResponse ar, @PathParam("heavyness") int heavyness) { response.setTimeout(50, MILLISECONDS); Observable.just(waitTime) .observeOn(Schedulers.computation()) .map(this::longProcess) .subscribe(result -> ar.resume(result), throwable -> ar.resume(throwable)); } private String longProcess(int heavyness) { ... } } Scheduler sur lequel faire le traitement ≈≈> executor
  284. @BriceDutheil @dwursteisen #DevoxxFR #Rx @Path("heavy-stuff") public static class WaitResource {

    @GET @Path("{heavyness}") public void wait(@Suspended AsyncResponse ar, @PathParam("heavyness") int heavyness) { response.setTimeout(50, MILLISECONDS); Observable.just(waitTime) .observeOn(Schedulers.computation()) .map(this::longProcess) .subscribe(result -> ar.resume(result), throwable -> ar.resume(throwable)); } private String longProcess(int heavyness) { ... } } Sera exécuté sur un Computation thread Thread de la servlet
  285. @BriceDutheil @dwursteisen #DevoxxFR #Rx @Path("heavy-stuff") public static class WaitResource {

    @GET @Path("{heavyness}") public void wait(@Suspended AsyncResponse ar, @PathParam("heavyness") int heavyness) { response.setTimeout(50, MILLISECONDS); Observable.just(waitTime) .observeOn(Schedulers.computation()) .map(this::longProcess) .subscribe(result -> ar.resume(result), throwable -> ar.resume(throwable)); } private String longProcess(int heavyness) { ... } } Travail
  286. @BriceDutheil @dwursteisen #DevoxxFR #Rx @Path("heavy-stuff") public static class WaitResource {

    @GET @Path("{heavyness}") public void wait(@Suspended AsyncResponse ar, @PathParam("heavyness") int heavyness) { response.setTimeout(50, MILLISECONDS); Observable.just(waitTime) .observeOn(Schedulers.computation()) .map(this::longProcess) .subscribe(result -> ar.resume(result), throwable -> ar.resume(throwable)); } private String longProcess(int heavyness) { ... } } onNext : résultat du traitement onError : termine la requête HTTP avec une erreur
  287. @BriceDutheil @dwursteisen #DevoxxFR #Rx Traitement toujours long = Attente pour

    le client
  288. @BriceDutheil @dwursteisen #DevoxxFR #Rx Traitement toujours long = Attente pour

    le client = Toujours besoin de threads
  289. @BriceDutheil @dwursteisen #DevoxxFR #Rx asyncResponse.setTimeout(50, MILLISECONDS); Retourne 503 Service Unavailable

  290. @BriceDutheil @dwursteisen #DevoxxFR #Rx asyncResponse.setTimeout(50, MILLISECONDS); Retourne 503 Service Unavailable

    MAIS le travail en cours est toujours actif
  291. @BriceDutheil @dwursteisen #DevoxxFR #Rx Introduire un TimeoutHandler Status HTTP Headers

    Entité personnalisée Arrêt / interruption des traitements ...
  292. @BriceDutheil @dwursteisen #DevoxxFR #Rx JAX-RS apporte des fonctionnalités riches mapping,

    routage, filtre, …
  293. @BriceDutheil @dwursteisen #DevoxxFR #Rx … mais limite l’intégration avec RxJava,

    par les contraintes de la spec.
  294. @BriceDutheil @dwursteisen #DevoxxFR #Rx JAX-RS nous limite dans notre capacité

    à découpler
  295. @BriceDutheil @dwursteisen #DevoxxFR #Rx Faut-il sortir de la stack JavaEE

    ?
  296. @BriceDutheil @dwursteisen #DevoxxFR #Rx Netflix, Spotify, Twitter, Uber, d’autres sont

    allés dans cette direction.
  297. @BriceDutheil @dwursteisen #DevoxxFR #Rx L’architecture microservices est une opportunité pour

    explorer les alternatives
  298. @BriceDutheil @dwursteisen #DevoxxFR #Rx Par exemple en Java, Vert.x, RxNetty,

  299. @BriceDutheil @dwursteisen #DevoxxFR #Rx RxNetty.createHttpServer(port, (request, response) -> { if

    (request.getHttpMethod() == HttpMethod.POST) { return request.getContent() .map(TehServer::parseBody) .map(TehServer::longStuff) .flatMap(result -> { response.setStatus(HttpResponseStatus.OK); response.writeString(result); return response.close(); }).ignoreElements(); } return response.close(false); });
  300. @BriceDutheil @dwursteisen #DevoxxFR #Rx RxNetty.createHttpServer(port, (request, response) -> { if

    (request.getHttpMethod() == HttpMethod.POST) { return request.getContent() .map(TehServer::parseBody) .map(TehServer::longStuff) .flatMap(result -> { response.setStatus(HttpResponseStatus.OK); response.writeString(result); return response.close(); }).ignoreElements(); } return response.close(false); }); Créer un serveur
  301. @BriceDutheil @dwursteisen #DevoxxFR #Rx RxNetty.createHttpServer(port, (request, response) -> { if

    (request.getHttpMethod() == HttpMethod.POST) { return request.getContent() .map(TehServer::parseBody) .map(TehServer::longStuff) .flatMap(result -> { response.setStatus(HttpResponseStatus.OK); response.writeString(result); return response.close(); }).ignoreElements(); } return response.close(false); }); Handler pour gérer la requête
  302. @BriceDutheil @dwursteisen #DevoxxFR #Rx RxNetty.createHttpServer(port, (request, response) -> { if

    (request.getHttpMethod() == HttpMethod.POST) { return request.getContent() .map(TehServer::parseBody) .map(TehServer::longStuff) .flatMap(result -> { response.setStatus(HttpResponseStatus.OK); response.writeString(result); return response.close(); }).ignoreElements(); } return response.close(false); }); Retourne un Observable
  303. @BriceDutheil @dwursteisen #DevoxxFR #Rx RxNetty.createHttpServer(port, (request, response) -> { if

    (request.getHttpMethod() == HttpMethod.POST) { return request.getContent() .map(TehServer::parseBody) .map(TehServer::longStuff) .flatMap(result -> { response.setStatus(HttpResponseStatus.OK); response.writeString(result); return response.close(); }).ignoreElements(); } return response.close(false); }); Travail sur la request avec RxJava
  304. @BriceDutheil @dwursteisen #DevoxxFR #Rx RxNetty s’utilise pour des services très

    simples websocket, http, tcp, …
  305. @BriceDutheil @dwursteisen #DevoxxFR #Rx RxNetty intégre RxJava au plus bas

    niveau
  306. @BriceDutheil @dwursteisen #DevoxxFR #Rx Un main() seulement

  307. @BriceDutheil @dwursteisen #DevoxxFR #Rx RxNetty offre une API de bas

    niveau pour plus de contrôle
  308. @BriceDutheil @dwursteisen #DevoxxFR #Rx … mais demande un effort appréciable

    sur la mise en oeuvre des aspects d’une API HTTP publique
  309. @BriceDutheil @dwursteisen #DevoxxFR #Rx Services composés

  310. @BriceDutheil @dwursteisen #DevoxxFR #Rx Composite API Service B Service A

  311. @BriceDutheil @dwursteisen #DevoxxFR #Rx Composite API

  312. @BriceDutheil @dwursteisen #DevoxxFR #Rx @GET public void wait(@Suspended final AsyncResponse

    response) { } Exemple : un stub JAX-RS
  313. @BriceDutheil @dwursteisen #DevoxxFR #Rx @GET public void wait(@Suspended final AsyncResponse

    response) { Observable.fromCallable(() -> makeInterestingValue()) .subscribe(response::resume); } Un Observable qui renvoie quelque chose sur AsyncResponse
  314. @BriceDutheil @dwursteisen #DevoxxFR #Rx @GET public void wait(@Suspended final AsyncResponse

    response) { Observable.fromCallable(() -> makeInterestingValue()) .flatMap(lastNumber -> { … }) .subscribe(response::resume); } Ici seront fait les appels
  315. @BriceDutheil @dwursteisen #DevoxxFR #Rx Composite API Service A

  316. @BriceDutheil @dwursteisen #DevoxxFR #Rx @GET public void wait(@Suspended final AsyncResponse

    response) { Observable.fromCallable(() -> makeInterestingValue()) .flatMap(lastNumber -> { Response aResponse = wsA(lastNumber); … }) .subscribe(response::resume); } Appel au service A
  317. @BriceDutheil @dwursteisen #DevoxxFR #Rx @GET public void wait(@Suspended final AsyncResponse

    response) { Observable.fromCallable(() -> makeInterestingValue()) .flatMap(lastNumber -> { Observable<Response> aObs = Observable.fromCallable(() -> wsA(lastNumber)); … }) .subscribe(response::resume); } Appel observable au service A
  318. @BriceDutheil @dwursteisen #DevoxxFR #Rx @GET public void wait(@Suspended final AsyncResponse

    response) { Observable.fromCallable(() -> makeInterestingValue()) .flatMap(lastNumber -> { Observable<String> aObs = Observable.fromCallable(() -> wsA(lastNumber)) .map(r -> r.readEntity(String.class)); … }) .subscribe(response::resume); } Transformation de la payload
  319. @BriceDutheil @dwursteisen #DevoxxFR #Rx Composite API Service B Service A

  320. @BriceDutheil @dwursteisen #DevoxxFR #Rx @GET public void wait(@Suspended final AsyncResponse

    response) { Observable.fromCallable(() -> makeInterestingValue()) .flatMap(lastNumber -> { Observable<String> aObs = Observable.fromCallable(() -> wsA(lastNumber)) .map(r -> r.readEntity(String.class)); Observable<String> bObs = Observable.fromCallable(() -> wsB(lastNumber)) .map(r -> r.readEntity(String.class)); … }) .subscribe(response::resume); } Appel observable au service B
  321. @BriceDutheil @dwursteisen #DevoxxFR #Rx Composite API Service B Service A

  322. @BriceDutheil @dwursteisen #DevoxxFR #Rx @GET public void wait(@Suspended final AsyncResponse

    response) { Observable.fromCallable(() -> makeInterestingValue()) .flatMap(lastNumber -> { Observable<String> aObs = Observable.fromCallable(() -> wsA(lastNumber)) .map(r -> r.readEntity(String.class)); Observable<String> bObs = Observable.fromCallable(() -> wsB(lastNumber)) .map(r -> r.readEntity(String.class)); return Observable.zip(aObs, bObs, (a, b) -> format("=> Result : %s, %s", a, b); }) .subscribe(response::resume); } Composition de A et de B
  323. @BriceDutheil @dwursteisen #DevoxxFR #Rx Si une exception est produite ?

  324. @BriceDutheil @dwursteisen #DevoxxFR #Rx @GET public void wait(@Suspended final AsyncResponse

    response) { Observable.fromCallable(() -> makeInterestingValue()) .flatMap(lastNumber -> { Observable<String> aObs = Observable.fromCallable(() -> wsA(lastNumber)) .map(r -> r.readEntity(String.class)); Observable<String> bObs = Observable.fromCallable(() -> wsB(lastNumber)) .map(r -> r.readEntity(String.class)); return Observable.zip(aObs, bObs, (a, b) -> format("=> Result : %s, %s", a, b); }) .subscribe(response::resume, response::resume); } Ajout d’un handler onError
  325. @BriceDutheil @dwursteisen #DevoxxFR #Rx Planifier les appels externes sur Schedulers.io()

  326. @BriceDutheil @dwursteisen #DevoxxFR #Rx @GET public void wait(@Suspended final AsyncResponse

    response) { Observable.fromCallable(() -> makeInterestingValue()) .flatMap(lastNumber -> { Observable<String> aObs = Observable.fromCallable(() -> wsA(lastNumber)) .subscribeOn(Schedulers.io()) .map(r -> r.readEntity(String.class)); Observable<String> bObs = Observable.fromCallable(() -> wsB(lastNumber)) .subscribeOn(Schedulers.io()) .map(r -> r.readEntity(String.class)); return Observable.zip(aObs, bObs, (a, b) -> format("=> Result : %s, %s", a, b); }) .subscribe(response::resume, response::resume); } Appels réseau exécutés sur des threads dédiés aux I/O
  327. @BriceDutheil @dwursteisen #DevoxxFR #Rx Composite API Service B Service A

    503
  328. @BriceDutheil @dwursteisen #DevoxxFR #Rx @GET public void wait(@Suspended final AsyncResponse

    response) { Observable.fromCallable(() -> makeInterestingValue()) .flatMap(lastNumber -> { Observable<String> aObs = Observable.fromCallable(() -> wsA(lastNumber)) .subscribeOn(Schedulers.io()) .flatMap(r -> { if (r.getStatus() == 503) { return Observable.error( new ServiceUnavailable("service A")); } return just(r.readEntity(String.class)); }); Observable<String> bObs = Observable.fromCallable(() -> wsB(lastNumber)) .subscribeOn(Schedulers.io()) .map(r -> r.readEntity(String.class)); return Observable.zip(aObs, bObs, (a, b) -> format("=> Result : %s, %s", a, b); }) .subscribe(response::resume, response::resume); } Gestion du statut HTTP 503
  329. @BriceDutheil @dwursteisen #DevoxxFR #Rx @GET public void wait(@Suspended final AsyncResponse

    response) { Observable.fromCallable(() -> makeInterestingValue()) .flatMap(lastNumber -> { Observable<String> aObs = Observable.fromCallable(() -> wsServiceA(lastNumber)) .subscribeOn(Schedulers.io()) .flatMap(r -> { if (r.getStatus() != 200) { return Observable.error( new ServiceUnavailable("service A")); } return Observable.just(r.readEntity(String.class)); }); Observable<String> bObs = Observable.fromCallable(() -> wsServiceB(lastNumber)) .subscribeOn(Schedulers.io()) .map(r -> r.readEntity(String.class)); return Observable.zip(aObs, bObs, (a, b) -> format("=> Result : %s, %s", a, b); }) .subscribe(response::resume, (error) -> { response.resume(error); }); } AsyncReponse.resume(Throwable) ⇒ 500 Server Error
  330. @BriceDutheil @dwursteisen #DevoxxFR #Rx @GET public void wait(@Suspended final AsyncResponse

    response) { Observable.fromCallable(() -> makeInterestingValue()) .flatMap(lastNumber -> { Observable<String> aObs = Observable.fromCallable(() -> wsServiceA(lastNumber)) .subscribeOn(Schedulers.io()) .flatMap(r -> { if (r.getStatus() != 200) { return Observable.error(new ServiceUnavailable("service A")); } return Observable.just(r.readEntity(String.class)); }); Observable<String> bObs = Observable.fromCallable(() -> wsServiceB(lastNumber)) .subscribeOn(Schedulers.io()) .map(r -> r.readEntity(String.class)); return Observable.zip(aObs, bObs, (a, b) -> format("=> Result : %s, %s", a, b); }) .subscribe(response::resume, (error) -> { response.resume(Response.serverError() .entity(error.getMessage()) .build()); }); } Personnalisation de la payload d’erreur
  331. @BriceDutheil @dwursteisen #DevoxxFR #Rx @GET public void wait(@Suspended final AsyncResponse

    response) { Observable.fromCallable(() -> makeInterestingValue()) .flatMap(lastNumber -> { Observable<String> aObs = Observable.fromCallable(() -> wsServiceA(lastNumber)) .subscribeOn(Schedulers.io()) .flatMap(r -> { if (r.getStatus() != 200) { return Observable.error(new ServiceUnavailable("service A")); } return Observable.just(r.readEntity(String.class)); }); Observable<String> bObs = Observable.fromCallable(() -> wsServiceB(lastNumber)) .subscribeOn(Schedulers.io()) .map(r -> r.readEntity(String.class)); return Observable.zip(aObs, bObs, (a, b) -> format("=> Result : %s, %s", a, b); }) .subscribe(response::resume, (error) -> { response.resume(Response.status(((HttpServiceEx) error).httpStatus) .entity(error.getMessage()) .build()); }); } Propagation du statut HTTP
  332. @BriceDutheil @dwursteisen #DevoxxFR #Rx Recommencer l’appel en cas d’erreur ?

  333. @BriceDutheil @dwursteisen #DevoxxFR #Rx @GET public void wait(@Suspended final AsyncResponse

    response) { Observable.fromCallable(() -> makeInterestingValue()) .flatMap(lastNumber -> { Observable<String> aObs = Observable.fromCallable(() -> wsServiceA(lastNumber)) .subscribeOn(Schedulers.io()) .flatMap(r -> { if (r.getStatus() != 200) { return Observable.error(new ServiceUnavailable("service A")); } return Observable.just(r.readEntity(String.class)); }) .retry(2); Observable<String> bObs = Observable.fromCallable(() -> wsServiceB(lastNumber)) .subscribeOn(Schedulers.io()) .map(r -> r.readEntity(String.class)); return Observable.zip(aObs, bObs, (a, b) -> format("=> Result : %s, %s", a, b); }) .subscribe(response::resume, (error) -> { response.resume(Response.status(((HttpServiceException) error).proposedHttpStatus) .entity(error.getMessage()) .build()); }); } Recommencera 2 fois
  334. @BriceDutheil @dwursteisen #DevoxxFR #Rx Quand une dépendance est hors-délai ?

  335. @BriceDutheil @dwursteisen #DevoxxFR #Rx Composite API Service B Service A

    Service B
  336. @BriceDutheil @dwursteisen #DevoxxFR #Rx @GET public void wait(@Suspended final AsyncResponse

    response) { Observable.fromCallable(() -> makeValue()) .flatMap(lastNumber -> { Observable<String> aObs = Observable.fromCallable(() -> wsServiceA(lastNumber)) .subscribeOn(Schedulers.io()) .flatMap(r -> { if (r.getStatus() != 200) { return Observable.error(new ServiceUnavailable("service A")); } return Observable.just(r.readEntity(String.class)); }) .retry(2); Observable<String> bObs = Observable.fromCallable(() -> wsServiceB(lastNumber)) .subscribeOn(Schedulers.io()) .map(r -> r.readEntity(String.class)) .timeout(500, MILLISECONDS, error(new ServiceTimeout("service B"))); return Observable.zip(aObs, bObs, (a, b) -> format("=> Result : %s, %s", a, b); }) .subscribe(response::resume, (error) -> { response.resume(Response.status(((HttpServiceException) error).proposedHttpStatus) .entity(error.getMessage()) .build()); }); } Défini un délai acceptable
  337. @BriceDutheil @dwursteisen #DevoxxFR #Rx C’est beaucoup de code technique !

  338. @BriceDutheil @dwursteisen #DevoxxFR #Rx Hystrix permet de résoudre ces challenges

    de façon plus pertinente
  339. @BriceDutheil @dwursteisen #DevoxxFR #Rx @GET public void wait(@Suspended final AsyncResponse

    response) { Observable.fromCallable(() -> new SecureRandom().nextInt(1000)) .flatMap(lastNumber -> { Observable<String> aObs = new ServiceACommand(lastNumber).observe(); Observable<String> bObs = new ServiceBCommand(lastNumber).observe(); return Observable.zip(aObs, bObs, (a, b) -> format("=> Result : %s, %s", a, b); }) .subscribe(response::resume, (error) -> { response.resume(Response.statusError() .entity(error.getMessage()) .build()); }); } Commandes Hystrix
  340. @BriceDutheil @dwursteisen #DevoxxFR #Rx Exécution immédiate et asynchrone avec .observe()

    Hot
  341. @BriceDutheil @dwursteisen #DevoxxFR #Rx Exécution retardée jusqu’à l'abonnement avec .toObservable()

    Cold
  342. @BriceDutheil @dwursteisen #DevoxxFR #Rx Une commande Hystrix ne doit jamais

    partir en erreur
  343. @BriceDutheil @dwursteisen #DevoxxFR #Rx @GET public void wait(@Suspended final AsyncResponse

    response) { Observable.fromCallable(() -> new SecureRandom().nextInt(1000)) .flatMap(lastNumber -> { Observable<String> aObs = new ServiceACommand(lastNumber).observe(); Observable<String> bObs = new ServiceBCommand(lastNumber).observe(); return Observable.zip(aObs, bObs, (a, b) -> format("=> Result : %s, %s", a, b); }) .subscribe(response::resume, (error) -> { response.resume(Response.serverError() .entity(error.getMessage()) .build()); }); } Erreur 500
  344. @BriceDutheil @dwursteisen #DevoxxFR #Rx class ServiceACommand extends HystrixObservableCommand<String> { private

    int number; protected ServiceACommand(int number) { super(Setter.withGroupKey(HystrixCommandGroupKey.Factory.asKey("work group")) .andCommandKey(HystrixCommandKey.Factory.asKey("work")) .andCommandPropertiesDefaults( HystrixCommandProperties.Setter().withExecutionTimeoutInMilliseconds(500))); this.number = number; } @Override protected Observable<String> construct() { return Observable.fromCallable(() -> wsServiceB(number)) .map(response -> response.readEntity(String.class)); } @Override protected Observable<String> resumeWithFallback() { return Observable.just("Service A too long"); } }
  345. @BriceDutheil @dwursteisen #DevoxxFR #Rx class ServiceACommand extends HystrixObservableCommand<String> { private

    int number; protected ServiceACommand(int number) { super(Setter.withGroupKey(HystrixCommandGroupKey.Factory.asKey("work group")) .andCommandKey(HystrixCommandKey.Factory.asKey("work")) .andCommandPropertiesDefaults( HystrixCommandProperties.Setter().withExecutionTimeoutInMilliseconds(500))); this.number = number; } @Override protected Observable<String> construct() { return Observable.fromCallable(() -> wsServiceB(number)) .map(response -> response.readEntity(String.class)); } @Override protected Observable<String> resumeWithFallback() { return Observable.just("Service A too long"); } } Commande Hystrix Type de retour
  346. @BriceDutheil @dwursteisen #DevoxxFR #Rx class ServiceACommand extends HystrixObservableCommand<String> { private

    int number; protected ServiceACommand(int number) { super(Setter.withGroupKey(HystrixCommandGroupKey.Factory.asKey("work group")) .andCommandKey(HystrixCommandKey.Factory.asKey("work")) .andCommandPropertiesDefaults( HystrixCommandProperties.Setter().withExecutionTimeoutInMilliseconds(500))); this.number = number; } @Override protected Observable<String> construct() { return Observable.fromCallable(() -> wsServiceB(number)) .map(response -> response.readEntity(String.class)); } @Override protected Observable<String> resumeWithFallback() { return Observable.just("Service A too long"); } } Commande Hystrix observable Construction
  347. @BriceDutheil @dwursteisen #DevoxxFR #Rx class ServiceACommand extends HystrixObservableCommand<String> { private

    int number; protected ServiceACommand(int number) { super(Setter.withGroupKey(HystrixCommandGroupKey.Factory.asKey("work group")) .andCommandKey(HystrixCommandKey.Factory.asKey("work")) .andCommandPropertiesDefaults( HystrixCommandProperties.Setter().withExecutionTimeoutInMilliseconds(500))); this.number = number; } @Override protected Observable<String> construct() { return Observable.fromCallable(() -> wsServiceB(number)) .map(response -> response.readEntity(String.class)); } @Override protected Observable<String> resumeWithFallback() { return Observable.just("Service A too long"); } } Appel observable
  348. @BriceDutheil @dwursteisen #DevoxxFR #Rx class ServiceACommand extends HystrixObservableCommand<String> { private

    int number; protected ServiceACommand(int number) { super(Setter.withGroupKey(HystrixCommandGroupKey.Factory.asKey("work group")) .andCommandKey(HystrixCommandKey.Factory.asKey("work")) .andCommandPropertiesDefaults( HystrixCommandProperties.Setter().withExecutionTimeoutInMilliseconds(500))); this.number = number; } @Override protected Observable<String> construct() { return Observable.fromCallable(() -> wsServiceB(number)) .map(response -> response.readEntity(String.class)); } @Override protected Observable<String> resumeWithFallback() { return Observable.just("Service A too long"); } } Plan de secours En cas d’erreur
  349. @BriceDutheil @dwursteisen #DevoxxFR #Rx class ServiceACommand extends HystrixObservableCommand<String> { private

    int number; protected ServiceACommand(int number) { super(Setter.withGroupKey(HystrixCommandGroupKey.Factory.asKey("work group")) .andCommandKey(HystrixCommandKey.Factory.asKey("work")) .andCommandPropertiesDefaults( HystrixCommandProperties.Setter().withExecutionTimeoutInMilliseconds(500))); this.number = number; } @Override protected Observable<String> construct() { return Observable.fromCallable(() -> wsServiceB(number)) .map(response -> response.readEntity(String.class)); } @Override protected Observable<String> resumeWithFallback() { return Observable.just("Service A too long"); } } Personnalisation des paramètres de la commande
  350. @BriceDutheil @dwursteisen #DevoxxFR #Rx Le coupe-circuit de Hystrix

  351. @BriceDutheil @dwursteisen #DevoxxFR #Rx Composite API Service B Service A

  352. @BriceDutheil @dwursteisen #DevoxxFR #Rx Fermé, les requêtes passent

  353. @BriceDutheil @dwursteisen #DevoxxFR #Rx Ouvert, la dépendance en erreur est

    isolée sur une période de temps
  354. @BriceDutheil @dwursteisen #DevoxxFR #Rx Avertir un client lorsque le circuit

    est ouvert
  355. @BriceDutheil @dwursteisen #DevoxxFR #Rx <T> void executeCommand(HystrixObservableCommand<T> command) { command.observe()

    .subscribe( result -> Response.ok(result).build(), throwable -> { if (throwable instanceof HystrixRuntimeException) { if (((HystrixRuntimeException) throwable).getFailureType() == FailureType.SHORTCIRCUIT) { Response.status(Status.SERVICE_UNAVAILABLE) .header(HttpHeaders.RETRY_AFTER, command.getProperties() .circuitBreakerSleepWindowInMilliseconds() .get() / 1000); } } Response.serverError().build(); }); } Pour une commande Hystrix
  356. @BriceDutheil @dwursteisen #DevoxxFR #Rx <T> void executeCommand(HystrixObservableCommand<T> command) { command.observe()

    .subscribe( result -> Response.ok(result).build(), throwable -> { if (throwable instanceof HystrixRuntimeException) { if (((HystrixRuntimeException) throwable).getFailureType() == FailureType.SHORTCIRCUIT) { Response.status(Status.SERVICE_UNAVAILABLE) .header(HttpHeaders.RETRY_AFTER, command.getProperties() .circuitBreakerSleepWindowInMilliseconds() .get() / 1000); } } Response.serverError().build(); }); } Si le problème vient de la commande Hystrix
  357. @BriceDutheil @dwursteisen #DevoxxFR #Rx private <T> void executeCommand(HystrixObservableCommand<T> command) {

    command.observe() .subscribe( result -> Response.ok(result).build(), throwable -> { if (throwable instanceof HystrixRuntimeException) { if (((HystrixRuntimeException) throwable).getFailureType() == FailureType.SHORTCIRCUIT) { Response.status(Status.SERVICE_UNAVAILABLE) .header(HttpHeaders.RETRY_AFTER, command.getProperties() .circuitBreakerSleepWindowInMilliseconds() .get() / 1000); } } Response.serverError().build(); }); } Et que le circuit est ouvert
  358. @BriceDutheil @dwursteisen #DevoxxFR #Rx private <T> void executeCommand(HystrixObservableCommand<T> command) {

    command.observe() .subscribe( result -> Response.ok(result).build(), throwable -> { if (throwable instanceof HystrixRuntimeException) { if (((HystrixRuntimeException) throwable).getFailureType() == FailureType.SHORTCIRCUIT) { Response.status(Status.SERVICE_UNAVAILABLE) .header(HttpHeaders.RETRY_AFTER, command.getProperties() .circuitBreakerSleepWindowInMilliseconds() .get() / 1000); } } Response.serverError().build(); }); } Alors proposer au client un prochain créneau
  359. @BriceDutheil @dwursteisen #DevoxxFR #Rx private <T> void executeCommand(HystrixObservableCommand<T> command) {

    command.observe() .subscribe( result -> Response.ok(result).build(), throwable -> { if (throwable instanceof HystrixRuntimeException) { if (((HystrixRuntimeException) throwable).getFailureType() == FailureType.SHORTCIRCUIT) { Response.status(Status.SERVICE_UNAVAILABLE) .header(HttpHeaders.RETRY_AFTER, command.getProperties() .circuitBreakerSleepWindowInMilliseconds() .get() / 1000); } } Response.serverError().build(); }); }
  360. @BriceDutheil @dwursteisen #DevoxxFR #Rx Hystrix demande une certaine dose de

    code Il est possible d’utiliser des annotations avec hystrix-javanica
  361. @BriceDutheil @dwursteisen #DevoxxFR #Rx

  362. @BriceDutheil @dwursteisen #DevoxxFR #Rx Hystrix protège un système distribué de

    dépendances en erreur ou hors-délai
  363. @BriceDutheil @dwursteisen #DevoxxFR #Rx Hystrix offre une variété d’outils

  364. @BriceDutheil @dwursteisen #DevoxxFR #Rx

  365. @BriceDutheil @dwursteisen #DevoxxFR #Rx Autre piste à explorer

  366. @BriceDutheil @dwursteisen #DevoxxFR #Rx Pression arrière

  367. @BriceDutheil @dwursteisen #DevoxxFR #Rx Production Consommation

  368. @BriceDutheil @dwursteisen #DevoxxFR #Rx Producteur Consommateur ?

  369. @BriceDutheil @dwursteisen #DevoxxFR #Rx Producteur Consommateur ?

  370. @BriceDutheil @dwursteisen #DevoxxFR #Rx Consommation .buffer() Production

  371. @BriceDutheil @dwursteisen #DevoxxFR #Rx Consommation .sample() Production

  372. @BriceDutheil @dwursteisen #DevoxxFR #Rx Consommation Isolation du consommateur Production

  373. @BriceDutheil @dwursteisen #DevoxxFR #Rx Consommation Pression arrière Isolation du consommateur

    Production
  374. @BriceDutheil @dwursteisen #DevoxxFR #Rx C’est le temps d’une pause Posez

    nous des questions directement ou via Twitter
  375. #DevoxxFR #Rx Reactive Extensions Sur notre application

  376. @BriceDutheil @dwursteisen #DevoxxFR #Rx Leçons apprises en utilisant les Reactive

    Extensions ?
  377. @BriceDutheil @dwursteisen #DevoxxFR #Rx RxJava est-il la solution à tout

    ?
  378. @BriceDutheil @dwursteisen #DevoxxFR #Rx RxJava est-il la solution à tout

    ? NON !
  379. @BriceDutheil @dwursteisen #DevoxxFR #Rx Les Streams Java 8 peuvent être

    plus intéressants.
  380. @BriceDutheil @dwursteisen #DevoxxFR #Rx Il est possible de faire cohabiter

    RxJava et Streams Java8
  381. @BriceDutheil @dwursteisen #DevoxxFR #Rx Observable.just(1, 2, 3, 4) .toList() .map(list

    -> { return list.stream() .filter(i -> i % 2 == 0) .collect(Collectors.toList()); }).subscribe();
  382. @BriceDutheil @dwursteisen #DevoxxFR #Rx Observable.just(1, 2, 3, 4) .toList() .map(list

    -> { return list.stream() .filter(i -> i % 2 == 0) .collect(Collectors.toList()); }).subscribe();
  383. @BriceDutheil @dwursteisen #DevoxxFR #Rx Observable.just(1, 2, 3, 4) .toList() .map(list

    -> { return list.stream() .filter(i -> i % 2 == 0) .collect(Collectors.toList()); }).subscribe();
  384. @BriceDutheil @dwursteisen #DevoxxFR #Rx List<Observable<?>> obs = new ArrayList<>(); obs.add(Observable.defer(()

    -> Observable.just(1, 2, 3))); obs.add(Observable.just(4, 5, 6)); obs.add(Observable.fromCallable(() -> 7)); Observable.merge(obs).subscribe();
  385. @BriceDutheil @dwursteisen #DevoxxFR #Rx List<Observable<?>> obs = new ArrayList<>(); obs.add(Observable.defer(()

    -> Observable.just(1, 2, 3))); obs.add(Observable.just(4, 5, 6)); obs.add(Observable.fromCallable(() -> 7)); Observable.merge(obs).subscribe();
  386. @BriceDutheil @dwursteisen #DevoxxFR #Rx List<Observable<?>> obs = new ArrayList<>(); obs.add(Observable.defer(()

    -> Observable.just(1, 2, 3))); obs.add(Observable.just(4, 5, 6)); obs.add(Observable.fromCallable(() -> 7)); Observable.merge(obs).subscribe();
  387. @BriceDutheil @dwursteisen #DevoxxFR #Rx List<Observable<?>> obs = new ArrayList<>(); obs.add(Observable.defer(()

    -> Observable.just(1, 2, 3))); obs.add(Observable.just(4, 5, 6)); obs.add(Observable.fromCallable(() -> 7)); Observable.merge(obs).subscribe();
  388. @BriceDutheil @dwursteisen #DevoxxFR #Rx Ce n’est pas parce que l’on

    peut le faire que l’on doit le faire.
  389. @BriceDutheil @dwursteisen #DevoxxFR #Rx Observable<Feed> feeds = Observable.merge( services.stream() .map(service

    -> service.newRequest().user(user).all().observe()) .map(feedObservable -> feedObservable .doOnError(throwable -> log.error(throwable.getMessage(), throwable)) .onErrorResumeNext(Observable.empty())) .collect(Collectors.toList())); feeds.subscribe();
  390. @BriceDutheil @dwursteisen #DevoxxFR #Rx Observable<Feed> feeds = Observable.merge( services.stream() .map(service

    -> service.newRequest().user(user).all().observe()) .map(feedObservable -> feedObservable .doOnError(throwable -> log.error(throwable.getMessage(), throwable)) .onErrorResumeNext(Observable.empty())) .collect(Collectors.toList())); feeds.subscribe(); RxJava Stream Java 8
  391. @BriceDutheil @dwursteisen #DevoxxFR #Rx Les lambdas simplifient la lecture du

    code
  392. @BriceDutheil @dwursteisen #DevoxxFR #Rx RxView.clickEvents(tapButton) .doOnNext(new Action1<ViewClickEvent>() { @Override public

    void call(ViewClickEvent onClickEvent) { LOGGER.debug("--------- GOT A TAP"); } }) .map(new Func1<ViewClickEvent, Integer>() { @Override public Integer call(ViewClickEvent onClickEvent) { return 1; } }).subscribe();
  393. @BriceDutheil @dwursteisen #DevoxxFR #Rx RxView.clickEvents(tapButton) .doOnNext(new Action1<ViewClickEvent>() { @Override public

    void call(ViewClickEvent onClickEvent) { LOGGER.debug("--------- GOT A TAP"); } }) .map(new Func1<ViewClickEvent, Integer>() { @Override public Integer call(ViewClickEvent onClickEvent) { return 1; } }).subscribe(); Code technique
  394. @BriceDutheil @dwursteisen #DevoxxFR #Rx RxView.clickEvents(tapButton) .doOnNext(event -> LOGGER.debug("--------- GOT A

    TAP")) .map(event -> 1) .subscribe(); 3 fois moins de code !
  395. @BriceDutheil @dwursteisen #DevoxxFR #Rx Sur Android, vous pouvez utiliser Kotlin

    ou Retrolambda ou Java 8 (Jack&Jill)
  396. @BriceDutheil @dwursteisen #DevoxxFR #Rx https://twitter.com/Piwai/status/717755657106001921

  397. @BriceDutheil @dwursteisen #DevoxxFR #Rx Observable.just(1, 2, 3) .flatMap(i -> {

    Observable<String> complex = webService.complexify(i) .map(str -> str.toLowerCase()) .retry(4); Observable<String> human = webService.toHumanString(i) .filter(str -> str.length() < 3) .take(1) .onErrorResumeNext(Observable.empty()) .defaultIfEmpty("Oups !"); return Observable.zip(complex, human, (c, h) -> c + h) .flatMap(str -> webService.frequencyOf(str)) .flatMapIterable(itr -> itr); }).subscribe();
  398. @BriceDutheil @dwursteisen #DevoxxFR #Rx Observable.just(1, 2, 3) .flatMap(i -> {

    Observable<String> complex = webService.complexify(i) .map(str -> str.toLowerCase()) .retry(4); Observable<String> human = webService.toHumanString(i) .filter(str -> str.length() < 3) .take(1) .onErrorResumeNext(Observable.empty()) .defaultIfEmpty("Oups !"); return Observable.zip(complex, human, (c, h) -> c + h) .flatMap(str -> webService.frequencyOf(str)) .flatMapIterable(itr -> itr); }).subscribe();
  399. @BriceDutheil @dwursteisen #DevoxxFR #Rx Observable.just(1, 2, 3) .flatMap(i -> {

    Observable<String> complex = webService.complexify(i) .map(str -> str.toLowerCase()) .retry(4); Observable<String> human = webService.toHumanString(i) .filter(str -> str.length() < 3) .take(1) .onErrorResumeNext(Observable.empty()) .defaultIfEmpty("Oups !"); return Observable.zip(complex, human, (c, h) -> c + h) .flatMap(str -> webService.frequencyOf(str)) .flatMapIterable(itr -> itr); }).subscribe();
  400. @BriceDutheil @dwursteisen #DevoxxFR #Rx Observable.just(1, 2, 3) .flatMap(i -> {

    Observable<String> complex = webService.complexify(i) .map(str -> str.toLowerCase()) .retry(4); Observable<String> human = webService.toHumanString(i) .filter(str -> str.length() < 3) .take(1) .onErrorResumeNext(Observable.empty()) .defaultIfEmpty("Oups !"); return Observable.zip(complex, human, (c, h) -> c + h) .flatMap(str -> webService.frequencyOf(str)) .flatMapIterable(itr -> itr); }).subscribe();
  401. @BriceDutheil @dwursteisen #DevoxxFR #Rx Observable.just(1, 2, 3) .flatMap(i -> {

    Observable<String> complex = webService.complexify(i) .map(str -> str.toLowerCase()) .retry(4); Observable<String> human = webService.toHumanString(i) .filter(str -> str.length() < 3) .take(1) .onErrorResumeNext(Observable.empty()) .defaultIfEmpty("Oups !"); return Observable.zip(complex, human, (c, h) -> c + h) .flatMap(str -> webService.frequencyOf(str)) .flatMapIterable(itr -> itr); }).subscribe();
  402. @BriceDutheil @dwursteisen #DevoxxFR #Rx Observable.just(1, 2, 3) .flatMap(i -> {

    Observable<String> complex = webService.complexify(i) .map(str -> str.toLowerCase()) .retry(4); Observable<String> human = webService.toHumanString(i) .filter(str -> str.length() < 3) .take(1) .onErrorResumeNext(Observable.empty()) .defaultIfEmpty("Oups !"); return Observable.zip(complex, human, (c, h) -> c + h) .flatMap(str -> webService.frequencyOf(str)) .flatMapIterable(itr -> itr); }).subscribe(); Trop complexe !
  403. @BriceDutheil @dwursteisen #DevoxxFR #Rx Observable.just(1, 2, 3) .flatMap(i -> {

    Observable<String> complex = webService.complexify(i) .map(str -> str.toLowerCase()) .retry(4); Observable<String> human = webService.toHumanString(i) .filter(str -> str.length() < 3) .take(1) .onErrorResumeNext(Observable.empty()) .defaultIfEmpty("Oups !"); return Observable.zip(complex, human, (c, h) -> c + h) .flatMap(str -> webService.frequencyOf(str)) .flatMapIterable(itr -> itr); }).subscribe(); Extraction
  404. @BriceDutheil @dwursteisen #DevoxxFR #Rx Observable.just(1, 2, 3) .flatMap(i -> transform(i))

    .subscribe(); public Observable<Object> transform(int i) { Observable<String> complex = webService.complexify(i) .map(str -> str.toLowerCase()) .retry(4); Observable<String> human = webService.toHumanString(i) .filter(str -> str.length() < 3) .take(1) .onErrorResumeNext(Observable.empty()) .defaultIfEmpty("Oups !"); return Observable.zip(complex, human, (c, h) -> c + h) .flatMap(str -> webService.frequencyOf(str)) .flatMapIterable(itr -> itr); }
  405. @BriceDutheil @dwursteisen #DevoxxFR #Rx Observable.just(1, 2, 3) .flatMap(i -> transform(i))

    .subscribe(); public Observable<Object> transform(int i) { Observable<String> complex = fetchComplexify(i); Observable<String> human = fetchHuman(i); return Observable.zip(complex, human, (c, h) -> c + h) .flatMap(str -> webService.frequencyOf(str)) .flatMapIterable(itr -> itr) }
  406. @BriceDutheil @dwursteisen #DevoxxFR #Rx Ne quittez pas la monade !

    (grossièrement)
  407. @BriceDutheil @dwursteisen #DevoxxFR #Rx Ne quittez pas la monade !

    Observable.just(1, 2, 3) .subscribe(arg -> { webservice.call(arg).subscribe(System.out::println); });
  408. @BriceDutheil @dwursteisen #DevoxxFR #Rx Observable.just(1, 2, 3) .subscribe(arg -> {

    webservice.call(arg).subscribe(System.out::println); }); Ne quittez pas la monade ! Observable imbriqué
  409. @BriceDutheil @dwursteisen #DevoxxFR #Rx Ne quittez pas la monade !

    Observable.just(1, 2, 3) .flatMap(arg -> webservice.call(arg)) .subscribe(System.out::println);
  410. @BriceDutheil @dwursteisen #DevoxxFR #Rx Ne quittez pas la monade !

    Observable.just(1, 2, 3) .flatMap(arg -> webservice.call(arg)) .subscribe(System.out::println);
  411. @BriceDutheil @dwursteisen #DevoxxFR #Rx flatMap permet d'émettre zéro, un, ou

    plusieurs évènements
  412. @BriceDutheil @dwursteisen #DevoxxFR #Rx Observable.interval(1, TimeUnit.SECONDS) .flatMap(tps -> Observable.just(1, 2,

    3)) .subscribe(System.out::println);
  413. @BriceDutheil @dwursteisen #DevoxxFR #Rx Observable.interval(1, TimeUnit.SECONDS) .flatMap(tps -> Observable.just(1, 2,

    3)) .subscribe(System.out::println);
  414. @BriceDutheil @dwursteisen #DevoxxFR #Rx Observable.interval(1, TimeUnit.SECONDS) .flatMap(tps -> Observable.just(1, 2,

    3)) .subscribe(System.out::println);
  415. @BriceDutheil @dwursteisen #DevoxxFR #Rx flatMap ne remplace pas un Observable

    par un autre.
  416. @BriceDutheil @dwursteisen #DevoxxFR #Rx Observable.interval(1, TimeUnit.SECONDS) .flatMap(tps -> Observable.just(1, 2,

    3)) .subscribe(System.out::println, Throwable::printStackTrace, () -> System.out.println("Completed !"));
  417. @BriceDutheil @dwursteisen #DevoxxFR #Rx Observable.interval(1, TimeUnit.SECONDS) .flatMap(tps -> Observable.just(1, 2,

    3)) .subscribe(System.out::println, Throwable::printStackTrace, () -> System.out.println("Completed !"));
  418. @BriceDutheil @dwursteisen #DevoxxFR #Rx Observable.interval(1, TimeUnit.SECONDS) .flatMap(tps -> Observable.just(1, 2,

    3)) .subscribe(System.out::println, Throwable::printStackTrace, () -> System.out.println("Completed !"));
  419. @BriceDutheil @dwursteisen #DevoxxFR #Rx Observable.interval(1, TimeUnit.SECONDS) .flatMap(tps -> Observable.just(1, 2,

    3)) .subscribe(System.out::println, Throwable::printStackTrace, () -> System.out.println("Completed !"));
  420. @BriceDutheil @dwursteisen #DevoxxFR #Rx Observable.interval(1, TimeUnit.SECONDS) .flatMap(tps -> Observable.just(1, 2,

    3)) .subscribe(System.out::println, Throwable::printStackTrace, () -> System.out.println("Completed !"));
  421. @BriceDutheil @dwursteisen #DevoxxFR #Rx Observable.interval(1, TimeUnit.SECONDS) .flatMap(tps -> Observable.just(1, 2,

    3)) .subscribe(System.out::println, Throwable::printStackTrace, () -> System.out.println("Completed !")); Cela n’arrivera jam ais !
  422. @BriceDutheil @dwursteisen #DevoxxFR #Rx Il faut comprendre le contrat Rx

    pour comprendre les opérateurs
  423. @BriceDutheil @dwursteisen #DevoxxFR #Rx Observable.interval(1, TimeUnit.SECONDS) .toList() .subscribe(System.out::println);

  424. @BriceDutheil @dwursteisen #DevoxxFR #Rx Observable.interval(1, TimeUnit.SECONDS) .toList() .subscribe(System.out::println);

  425. @BriceDutheil @dwursteisen #DevoxxFR #Rx Observable.interval(1, TimeUnit.SECONDS) .toList() .subscribe(System.out::println);

  426. @BriceDutheil @dwursteisen #DevoxxFR #Rx toList() toList()

  427. @BriceDutheil @dwursteisen #DevoxxFR #Rx toList() toList()

  428. @BriceDutheil @dwursteisen #DevoxxFR #Rx Observable.interval

  429. @BriceDutheil @dwursteisen #DevoxxFR #Rx Observable.interval Fin du flux ?

  430. @BriceDutheil @dwursteisen #DevoxxFR #Rx Observable.interval(1, TimeUnit.SECONDS) .toList() .subscribe(System.out::println);

  431. @BriceDutheil @dwursteisen #DevoxxFR #Rx Hot versus Cold

  432. @BriceDutheil @dwursteisen #DevoxxFR #Rx Un Cold Observable créé l'émetteur

  433. @BriceDutheil @dwursteisen #DevoxxFR #Rx Un Hot Observable utilise un émetteur

    existant
  434. @BriceDutheil @dwursteisen #DevoxxFR #Rx Observable.create(subscriber -> { Timer timer =

    new Timer(); timer.onTick(() -> subscriber.onNext("tick")); }).subscribe();
  435. @BriceDutheil @dwursteisen #DevoxxFR #Rx Observable.create(subscriber -> { Timer timer =

    new Timer(); timer.onTick(() -> subscriber.onNext("tick")); }).subscribe();
  436. @BriceDutheil @dwursteisen #DevoxxFR #Rx Observable.create(subscriber -> { Timer timer =

    new Timer(); timer.onTick(() -> subscriber.onNext("tick")); }).subscribe();
  437. @BriceDutheil @dwursteisen #DevoxxFR #Rx Observable.create(subscriber -> { Timer timer =

    new Timer(); timer.onTick(() -> subscriber.onNext("tick")); }).subscribe(); Cold
  438. @BriceDutheil @dwursteisen #DevoxxFR #Rx Timer timer = new Timer(); Observable.create(subscriber

    -> { timer.onTick(() -> subscriber.onNext("tick")); }).subscribe();
  439. @BriceDutheil @dwursteisen #DevoxxFR #Rx Timer timer = new Timer(); Observable.create(subscriber

    -> { timer.onTick(() -> subscriber.onNext("tick")); }).subscribe();
  440. @BriceDutheil @dwursteisen #DevoxxFR #Rx Timer timer = new Timer(); Observable.create(subscriber

    -> { timer.onTick(() -> subscriber.onNext("tick")); }).subscribe();
  441. @BriceDutheil @dwursteisen #DevoxxFR #Rx Timer timer = new Timer(); Observable.create(subscriber

    -> { timer.onTick(() -> subscriber.onNext("tick")); }).subscribe(); Hot
  442. @BriceDutheil @dwursteisen #DevoxxFR #Rx Observable<String> response = ws.call();

  443. @BriceDutheil @dwursteisen #DevoxxFR #Rx Observable<String> response = ws.call(); response .map(str

    -> str.toLowerCase()) .subscribe(System.out::println);
  444. @BriceDutheil @dwursteisen #DevoxxFR #Rx Observable<String> response = ws.call(); response .map(str

    -> str.toLowerCase()) .subscribe(System.out::println); response .subscribe(System.out::println);
  445. @BriceDutheil @dwursteisen #DevoxxFR #Rx Observable<String> response = ws.call(); response .map(str

    -> str.toLowerCase()) .subscribe(System.out::println); response .subscribe(System.out::println);
  446. @BriceDutheil @dwursteisen #DevoxxFR #Rx Observable<String> response = ws.call(); response .map(str

    -> str.toLowerCase()) .subscribe(System.out::println); response .subscribe(System.out::println);
  447. @BriceDutheil @dwursteisen #DevoxxFR #Rx Observable<String> response = ws.call(); response .map(str

    -> str.toLowerCase()) .subscribe(System.out::println); response .subscribe(System.out::println); Cold
  448. @BriceDutheil @dwursteisen #DevoxxFR #Rx Observable<String> response = ws.call(); response .map(str

    -> str.toLowerCase()) .subscribe(System.out::println); response .subscribe(System.out::println); Cold Création d’un appel réseau
  449. @BriceDutheil @dwursteisen #DevoxxFR #Rx Observable<String> response = ws.call(); response .map(str

    -> str.toLowerCase()) .subscribe(System.out::println); response .subscribe(System.out::println); Cold Création d’un second appel réseau Création d’un appel réseau
  450. @BriceDutheil @dwursteisen #DevoxxFR #Rx Utilisation de MDC pour les logs

    : attention aux mauvaises surprises.
  451. @BriceDutheil @dwursteisen #DevoxxFR #Rx MDC.put("ID", "AAA"); LOG.warning("first_observable: {}", l);

  452. @BriceDutheil @dwursteisen #DevoxxFR #Rx WARN AAA [2014-11-10 02:57:24,566] first_observable: 9

    MDC.put("ID", "AAA"); LOG.warning("first_observable: {}", l);
  453. @BriceDutheil @dwursteisen #DevoxxFR #Rx WARN AAA [2014-11-10 02:57:24,566] first_observable: 9

    MDC.put("ID", "AAA"); LOG.warning("first_observable: {}", l); Utilisation de ThreadLocal
  454. @BriceDutheil @dwursteisen #DevoxxFR #Rx WARN AAA [2014-11-10 02:57:24,566] first_observable: 9

    WARN BBB [2014-11-10 02:57:24,576] second_observable: 1 WARN AAA [2014-11-10 02:57:25,566] first_observable: 10 WARN CCC [2014-11-10 02:57:25,576] third_observable: 2 Contexte synchrone
  455. @BriceDutheil @dwursteisen #DevoxxFR #Rx WARN AAA [2014-11-10 02:57:24,566] first_observable: 9

    WARN BBB [2014-11-10 02:57:24,576] second_observable: 1 WARN AAA [2014-11-10 02:57:25,566] first_observable: 10 WARN CCC [2014-11-10 02:57:25,576] third_observable: 2 Contexte synchrone
  456. @BriceDutheil @dwursteisen #DevoxxFR #Rx WARN AAA [2014-11-10 02:57:24,566] first_observable: 9

    WARN BBB [2014-11-10 02:57:24,576] third_observable: 1 WARN AAA [2014-11-10 02:57:25,566] second_observable: 10 WARN BBB [2014-11-10 02:57:25,576] third_observable: 2 Contexte asynchrone
  457. @BriceDutheil @dwursteisen #DevoxxFR #Rx WARN AAA [2014-11-10 02:57:24,566] first_observable: 9

    WARN BBB [2014-11-10 02:57:24,576] third_observable: 1 WARN AAA [2014-11-10 02:57:25,566] second_observable: 10 WARN BBB [2014-11-10 02:57:25,576] third_observable: 2 Contexte asynchrone Interleaving
  458. @BriceDutheil @dwursteisen #DevoxxFR #Rx Il est nécessaire de remettre en

    place le contexte lors d’un changement de contexte.
  459. @BriceDutheil @dwursteisen #DevoxxFR #Rx RxJavaPlugins.getInstance().registerSchedulersHook(new RxJavaSchedulersHook() { @Override public Action0

    onSchedule(Action0 action) { return super.onSchedule(() -> { MDC.setContextMap(mdcContextMap); try { action.call(); } finally { MDC.clear(); } }); } }); http://blog.mabn.pl/2014/11/rxjava-logback-and-mdc-threadlocal.html (version courte)
  460. @BriceDutheil @dwursteisen #DevoxxFR #Rx RxJavaPlugins.getInstance().registerSchedulersHook(new RxJavaSchedulersHook() { @Override public Action0

    onSchedule(Action0 action) { return super.onSchedule(() -> { MDC.setContextMap(mdcContextMap); try { action.call(); } finally { MDC.clear(); } }); } }); http://blog.mabn.pl/2014/11/rxjava-logback-and-mdc-threadlocal.html (version courte) À chaque planification de tâche
  461. @BriceDutheil @dwursteisen #DevoxxFR #Rx RxJavaPlugins.getInstance().registerSchedulersHook(new RxJavaSchedulersHook() { @Override public Action0

    onSchedule(Action0 action) { return super.onSchedule(() -> { MDC.setContextMap(mdcContextMap); try { action.call(); } finally { MDC.clear(); } }); } }); http://blog.mabn.pl/2014/11/rxjava-logback-and-mdc-threadlocal.html (version courte) À chaque planification de tâche On planifie une autre tâche
  462. @BriceDutheil @dwursteisen #DevoxxFR #Rx RxJavaPlugins.getInstance().registerSchedulersHook(new RxJavaSchedulersHook() { @Override public Action0

    onSchedule(Action0 action) { return super.onSchedule(() -> { MDC.setContextMap(mdcContextMap); try { action.call(); } finally { MDC.clear(); } }); } }); http://blog.mabn.pl/2014/11/rxjava-logback-and-mdc-threadlocal.html (version courte) À chaque planification de tâche On planifie une autre tâche Qui remet le contexte et qui exécute notre tâche
  463. @BriceDutheil @dwursteisen #DevoxxFR #Rx Observable.interval(1, TimeUnit.SECONDS) .flatMap(i -> { try

    { RandomException.mayThrowException(); return Observable.empty(); } catch (IOException e) { return Observable.error(e); } }).subscribe(System.out::println, e -> { /* ... */ });
  464. @BriceDutheil @dwursteisen #DevoxxFR #Rx Observable.interval(1, TimeUnit.SECONDS) .flatMap(i -> { try

    { RandomException.mayThrowException(); return Observable.empty(); } catch (IOException e) { return Observable.error(e); } }).subscribe(System.out::println, e -> { /* ... */ }); Quelle valeur a généré l’exception ?
  465. @BriceDutheil @dwursteisen #DevoxxFR #Rx Observable.interval(1, TimeUnit.SECONDS) .flatMap(i -> { try

    { RandomException.mayThrowException(); return Observable.empty(); } catch (IOException e) { return Observable.error(OnErrorThrowable.addValueAsLastCause(e, i)); } }).subscribe(System.out::println, e -> { /* ... */ }); Associe l’exception avec une valeur
  466. @BriceDutheil @dwursteisen #DevoxxFR #Rx Observable.interval(1, TimeUnit.SECONDS) .flatMap(i -> { try

    { RandomException.mayThrowException(); return Observable.empty(); } catch (IOException e) { return Observable.error(OnErrorThrowable.addValueAsLastCause(e, i)); } }).subscribe(System.out::println, e -> { Object value = OnErrorThrowable.from(e).getValue(); }); Récupère la valeur depuis l’exception
  467. @BriceDutheil @dwursteisen #DevoxxFR #Rx Grâce à OnErrorThrowable, il est possible

    d'accéder à la valeur responsable de l’exception
  468. @BriceDutheil @dwursteisen #DevoxxFR #Rx public interface WebService { Observable<String> call();

    } Va émettre une valeur ? Va émettre plusieurs valeurs ? Va émettre aucune valeur ?
  469. @BriceDutheil @dwursteisen #DevoxxFR #Rx Single = onSuccess | onError (Actuellement

    en @Beta)
  470. @BriceDutheil @dwursteisen #DevoxxFR #Rx Completable = onCompleted | onError (Actuellement

    en @Experimental)
  471. @BriceDutheil @dwursteisen #DevoxxFR #Rx public interface WebService { Observable<String> list();

    Single<String> get(); Completable perform(); } Va émettre un ensemble de valeurs va émettre une unique valeur ne va pas émettre de valeur
  472. @BriceDutheil @dwursteisen #DevoxxFR #Rx Single et Completable permettent d’exprimer une

    intention.
  473. @BriceDutheil @dwursteisen #DevoxxFR #Rx La souscription permet de signaler le

    désabonnement à un flux
  474. @BriceDutheil @dwursteisen #DevoxxFR #Rx Observable.interval(1, TimeUnit.SECONDS) .subscribe(System.out::println);

  475. @BriceDutheil @dwursteisen #DevoxxFR #Rx Subscription interval = Observable.interval(1, TimeUnit.SECONDS) .subscribe(System.out::println);

    Tada !
  476. @BriceDutheil @dwursteisen #DevoxxFR #Rx Subscription interval = Observable.interval(1, TimeUnit.SECONDS) .subscribe(System.out::println);

    interval.unsubscribe(); Stop le flux
  477. @BriceDutheil @dwursteisen #DevoxxFR #Rx CompositeSubscription permet de gérer un ensemble

    d’abonnements
  478. @BriceDutheil @dwursteisen #DevoxxFR #Rx Subscription flux1 = Observable.interval(1, TimeUnit.SECONDS) .subscribe(System.out::println);

    Subscription flux2 = Observable.interval(100, TimeUnit.MILLISECONDS) .subscribe(System.err::println);
  479. @BriceDutheil @dwursteisen #DevoxxFR #Rx Subscription flux1 = Observable.interval(1, TimeUnit.SECONDS) .subscribe(System.out::println);

    Subscription flux2 = Observable.interval(100, TimeUnit.MILLISECONDS) .subscribe(System.err::println); flux1.unsubscribe(); flux2.unsubscribe();
  480. @BriceDutheil @dwursteisen #DevoxxFR #Rx Subscription flux1 = Observable.interval(1, TimeUnit.SECONDS) .subscribe(System.out::println);

    Subscription flux2 = Observable.interval(100, TimeUnit.MILLISECONDS) .subscribe(System.err::println); CompositeSubscription subscription = new CompositeSubscription(); subscription.add(flux1); subscription.add(flux2); Compose les abonnements
  481. @BriceDutheil @dwursteisen #DevoxxFR #Rx Subscription flux1 = Observable.interval(1, TimeUnit.SECONDS) .subscribe(System.out::println);

    Subscription flux2 = Observable.interval(100, TimeUnit.MILLISECONDS) .subscribe(System.err::println); CompositeSubscription subscription = new CompositeSubscription(); subscription.add(flux1); subscription.add(flux2); subscription.unsubscribe(); Désabonne les flux
  482. @BriceDutheil @dwursteisen #DevoxxFR #Rx private CompositeSubscription subscription; @Override public void

    onResume() { Subscription interval = Observable.interval(1, TimeUnit.SECONDS) .subscribe(/* ... */); Subscription click = clicks() .subscribe(/*...*/); subscription = new CompositeSubscription(); subscription.add(interval); subscription.add(click); } @Override public void onPause() { subscription.unsubscribe(); } Désabonne les flux
  483. @BriceDutheil @dwursteisen #DevoxxFR #Rx Du code peut être exécuté lors

    du désabonnement
  484. @BriceDutheil @dwursteisen #DevoxxFR #Rx Observable.create(subscriber -> { subscriber.add(new Subscription() {

    @Override public void unsubscribe() { // fermeture du flux } }); }); Fermeture des ressources, etc
  485. @BriceDutheil @dwursteisen #DevoxxFR #Rx Observable.create(subscriber -> { subscriber.add(new Subscription() {

    @Override public void unsubscribe() { // fermeture du flux } }); });
  486. @BriceDutheil @dwursteisen #DevoxxFR #Rx Mais comment le faire sans utiliser

    Observable.create ?
  487. @BriceDutheil @dwursteisen #DevoxxFR #Rx Observable.using( () -> new DB(), db

    -> Observable.just(db.doQuery()), db -> db.closeDb()) .subscribe();
  488. @BriceDutheil @dwursteisen #DevoxxFR #Rx Observable.using( () -> new DB(), db

    -> Observable.just(db.doQuery()), db -> db.closeDb()) .subscribe(); Fermeture des ressources, etc
  489. @BriceDutheil @dwursteisen #DevoxxFR #Rx Avant de se dire au revoir

  490. @BriceDutheil @dwursteisen #DevoxxFR #Rx Les autres talks sur les Reactive

    Extensions - A lite Rx API for the JVM Hands On Labs - Mercredi 9h30 - Baptême du feu avec Rx Hands On Labs - Vendredi 11h00 Alexandre Victoor Florent Le Gall Sebastien Deleuze Stephane Maldini
  491. @BriceDutheil @dwursteisen #DevoxxFR #Rx Remerciements Gautier Mechling @Nilhcem Simon Baslé

    @simonbasle David Karnok @akarnokd Dan Lew @danlew42
  492. @BriceDutheil @dwursteisen #DevoxxFR #Rx Retrouvez nous dans les couloirs de

    Devoxx
  493. @BriceDutheil @dwursteisen #DevoxxFR #Rx Y a t’il des points que

    nous n’aurions pas abordés ?
  494. @BriceDutheil @dwursteisen #DevoxxFR #Rx Merci

  495. @BriceDutheil @dwursteisen #DevoxxFR #Rx

  496. @BriceDutheil @dwursteisen #DevoxxFR #Rx