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

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

Note cette présentation est la même que https://speakerdeck.com/dwursteisen/architecture-decouplee-grace-aux-reactive-extensions

Vidéo : https://youtu.be/4To7s3qln2s

F31c7fbcbb0766d0632d96fd7e74b649?s=128

Brice Dutheil

April 25, 2016
Tweet

Transcript

  1. 5.

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

    Extensions en théorie en pratique sur notre application
  2. 6.

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

    Extensions en théorie en pratique sur notre application Pause
  3. 9.

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

    Énumération des opérateurs Mieux que ces alternatives ?
  4. 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
  5. 30.

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

    TCP Requête # nouvelle requête connexion établie prête à être consommée
  6. 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 =
  7. 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 =
  8. 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
  9. 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
  10. 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
  11. 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
  12. 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
  13. 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
  14. 48.

    @BriceDutheil @dwursteisen #DevoxxFR #Rx Tomcat client OS Requête # nouvelle

    requête Acceptor Thread Attend un slot disponible Thread war Executor full
  15. 49.

    @BriceDutheil @dwursteisen #DevoxxFR #Rx Point de vue du CPU Pas

    d’instruction à exécuter Le système attend sur des I/Os
  16. 52.

    @BriceDutheil @dwursteisen #DevoxxFR #Rx Localement aussi Lecture/Écriture sur disque -

    ex : swap Panne hardware - ex : disque en panne augmente la latence
  17. 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
  18. 60.

    @BriceDutheil @dwursteisen #DevoxxFR #Rx Tomcat / NIO client OS Requête

    # nouvelle requête Acceptor Thread Thread Poller Thread Requête # war
  19. 61.

    @BriceDutheil @dwursteisen #DevoxxFR #Rx Tomcat / NIO client OS Requête

    # nouvelle requête Acceptor Thread Thread Poller Thread Requête # war
  20. 72.

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

    • Executors • Future • Embarquer une techno à la ActiveMQ ?
  21. 73.

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

    • Executors • Future • Embarquer une techno à la ActiveMQ ? • Problème de lourdeur, overhead
  22. 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
  23. 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 ?
  24. 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 ?
  25. 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
  26. 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
  27. 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
  28. 104.

    @BriceDutheil @dwursteisen #DevoxxFR #Rx Pour quoi faire ? future1.get(); future2.get();

    future3.get(); future4.get(); future5.get(); Ordonnancement optimal ?
  29. 106.

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

    { @Override public void completed(HttpResponse response) { } });
  30. 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) { } }); } });
  31. 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) { } }); } });
  32. 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
  33. 122.
  34. 123.

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

    Observable<json> Observable<Integer> Observable<Click>
  35. 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"); } });
  36. 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"); } });
  37. 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"); } });
  38. 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"); } });
  39. 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") );
  40. 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
  41. 139.
  42. 140.

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

    * 2) .take(2) .defaultIfEmpty(0) .subscribe(); Opérateur map
  43. 141.

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

    * 2) .take(2) .defaultIfEmpty(0) .subscribe(); Opérateur take
  44. 142.

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

    * 2) .take(2) .defaultIfEmpty(0) .subscribe(); Opérateur defaultIfEmpty
  45. 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 ?
  46. 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)
  47. 155.

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

    "bonjour", "Hello") .map(str -> str.toLowerCase()) .distinct() .collect(Collectors.toList()); Factory
  48. 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
  49. 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
  50. 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)
  51. 163.
  52. 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
  53. 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)
  54. 170.

    @BriceDutheil @dwursteisen #DevoxxFR #Rx Effets de bord List<Integer> result =

    new ArrayList<>(); Observable.just(1, 2, 3) .map(i -> result.add(i)) .subscribe();
  55. 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
  56. 175.

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

    .map(i -> { LOGGER.info("value : " + i); return i; }) .subscribe();
  57. 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
  58. 178.

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

    // explicite .doOnNext(i -> LOGGER.info("Valeur :" + i)) .subscribe();
  59. 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();
  60. 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();
  61. 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();
  62. 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();
  63. 184.

    @BriceDutheil @dwursteisen #DevoxxFR #Rx // attention ! état partagé !

    AtomicLong errors = new AtomicLong(0); Observable.just(1, 2, 3) .doOnError(ex -> errors.incrementAndGet()) .subscribe();
  64. 185.

    @BriceDutheil @dwursteisen #DevoxxFR #Rx // attention ! état partagé !

    AtomicLong errors = new AtomicLong(0); Observable.just(1, 2, 3) .doOnError(ex -> errors.incrementAndGet()) .subscribe();
  65. 187.
  66. 188.

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

    Observable.defer() Observable.using() Observable.create() Simple d’utilisation
  67. 189.

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

    Observable.defer() Observable.using() Observable.create() Complexe d’utilisation
  68. 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
  69. 206.

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

    -> Observable.just(db.doQuery()), db -> db.closeDb()) .subscribe();
  70. 207.

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

    -> Observable.just(db.doQuery()), db -> db.closeDb()) .subscribe(); Création d’une ressource
  71. 208.

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

    -> Observable.just(db.doQuery()), db -> db.closeDb()) .subscribe(); Émission d’un résultat
  72. 209.

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

    -> Observable.just(db.doQuery()), db -> db.closeDb()) .subscribe(); Fermeture de la ressource
  73. 212.

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

    subscriber.setProducer(n -> { subscriber.onNext(/* ... */); // ... subscriber.onCompleted(); }); }).subscribe();
  74. 213.

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

    subscriber.setProducer(n -> { subscriber.onNext(/* ... */); // ... subscriber.onCompleted(); }); }).subscribe(); ? ? ?
  75. 214.

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

    subscriber.setProducer(n -> { subscriber.onNext(/* ... */); // ... subscriber.onCompleted(); }); }).subscribe(); Désabonnement Backpressure Contrat Rx
  76. 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();
  77. 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)
  78. 221.

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

    new DB(); subscriber.add(Subscriptions.create(() -> db.closeDb())); subscriber.onNext(db); subscriber.onCompleted(); }).subscribe();
  79. 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
  80. 223.

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

    -> Observable.just(db.doQuery()), db -> db.closeDb()) .subscribe(); Plus simple !
  81. 225.
  82. 228.
  83. 233.

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

    servlets OS Tomcat / BIO client Requête # Acceptor Thread Thread war
  84. 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
  85. 238.

    @BriceDutheil @dwursteisen #DevoxxFR #Rx RxJava permet un contrôle explicite de

    la concurrence avec observeOn() ou avec subscribeOn()
  86. 240.

    @BriceDutheil @dwursteisen #DevoxxFR #Rx method() { Observable.from(items) .observeOn( ) .map(this::transform)

    .flatMap(this::computeRelativeItems) .subscribe(response::resume, response::resume); } Thread courant
  87. 241.

    @BriceDutheil @dwursteisen #DevoxxFR #Rx method() { Observable.from(items) .observeOn( ) .map(this::transform)

    .flatMap(this::computeRelativeItems) .subscribe(response::resume, response::resume); } Thread courant
  88. 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
  89. 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
  90. 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
  91. 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
  92. 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
  93. 253.

    @BriceDutheil @dwursteisen #DevoxxFR #Rx Il ne peut y avoir qu’un

    seul subscribeOn() Premier arrivé, premier servi !
  94. 255.

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

    .filter(…) Élément <> .merge(…) Élément ‘a’ .map(str -> …)
  95. 256.

    @BriceDutheil @dwursteisen #DevoxxFR #Rx computation() Nombre de coeur CPU .map(str

    -> …) Élément # .filter(…) Élément <> .merge(…) Élément ‘a’ .map(str -> …)
  96. 257.

    @BriceDutheil @dwursteisen #DevoxxFR #Rx computation() Nombre de coeur CPU .debounce(…)

    .filter(e -> ...) .merge(...) Élément # .filter(…) Élément <> .merge(…)
  97. 266.

    @BriceDutheil @dwursteisen #DevoxxFR #Rx io() DB Network Internet Élément #

    .filter(…) Élément <> .merge(…) Élément ‘a’ .flatMap(…)
  98. 267.

    @BriceDutheil @dwursteisen #DevoxxFR #Rx io() DB Network Internet Élément #

    .filter(…) Élément <> .merge(…) Élément ‘a’ .flatMap(…)
  99. 268.

    @BriceDutheil @dwursteisen #DevoxxFR #Rx io() DB Network Internet Élément #

    .filter(…) Élément <> .merge(…) Élément ‘a’ .flatMap(…)
  100. 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(); } }
  101. 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
  102. 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); } }); } }
  103. 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)
  104. 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
  105. 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
  106. 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
  107. 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) { ... } }
  108. 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
  109. 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
  110. 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
  111. 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
  112. 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
  113. 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
  114. 291.

    @BriceDutheil @dwursteisen #DevoxxFR #Rx Introduire un TimeoutHandler Status HTTP Headers

    Entité personnalisée Arrêt / interruption des traitements ...
  115. 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); });
  116. 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
  117. 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
  118. 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
  119. 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
  120. 308.

    @BriceDutheil @dwursteisen #DevoxxFR #Rx … mais demande un effort appréciable

    sur la mise en oeuvre des aspects d’une API HTTP publique
  121. 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
  122. 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
  123. 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
  124. 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
  125. 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
  126. 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
  127. 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
  128. 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
  129. 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
  130. 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
  131. 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
  132. 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
  133. 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
  134. 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
  135. 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
  136. 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
  137. 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
  138. 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"); } }
  139. 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
  140. 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
  141. 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
  142. 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
  143. 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
  144. 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
  145. 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
  146. 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
  147. 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
  148. 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(); }); }
  149. 360.

    @BriceDutheil @dwursteisen #DevoxxFR #Rx Hystrix demande une certaine dose de

    code Il est possible d’utiliser des annotations avec hystrix-javanica
  150. 374.
  151. 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();
  152. 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();
  153. 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();
  154. 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();
  155. 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();
  156. 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();
  157. 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();
  158. 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();
  159. 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
  160. 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();
  161. 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
  162. 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();
  163. 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();
  164. 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();
  165. 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();
  166. 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();
  167. 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 !
  168. 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
  169. 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); }
  170. 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) }
  171. 407.

    @BriceDutheil @dwursteisen #DevoxxFR #Rx Ne quittez pas la monade !

    Observable.just(1, 2, 3) .subscribe(arg -> { webservice.call(arg).subscribe(System.out::println); });
  172. 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é
  173. 409.

    @BriceDutheil @dwursteisen #DevoxxFR #Rx Ne quittez pas la monade !

    Observable.just(1, 2, 3) .flatMap(arg -> webservice.call(arg)) .subscribe(System.out::println);
  174. 410.

    @BriceDutheil @dwursteisen #DevoxxFR #Rx Ne quittez pas la monade !

    Observable.just(1, 2, 3) .flatMap(arg -> webservice.call(arg)) .subscribe(System.out::println);
  175. 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 !"));
  176. 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 !"));
  177. 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 !"));
  178. 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 !"));
  179. 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 !"));
  180. 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 !
  181. 434.

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

    new Timer(); timer.onTick(() -> subscriber.onNext("tick")); }).subscribe();
  182. 435.

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

    new Timer(); timer.onTick(() -> subscriber.onNext("tick")); }).subscribe();
  183. 436.

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

    new Timer(); timer.onTick(() -> subscriber.onNext("tick")); }).subscribe();
  184. 437.

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

    new Timer(); timer.onTick(() -> subscriber.onNext("tick")); }).subscribe(); Cold
  185. 438.

    @BriceDutheil @dwursteisen #DevoxxFR #Rx Timer timer = new Timer(); Observable.create(subscriber

    -> { timer.onTick(() -> subscriber.onNext("tick")); }).subscribe();
  186. 439.

    @BriceDutheil @dwursteisen #DevoxxFR #Rx Timer timer = new Timer(); Observable.create(subscriber

    -> { timer.onTick(() -> subscriber.onNext("tick")); }).subscribe();
  187. 440.

    @BriceDutheil @dwursteisen #DevoxxFR #Rx Timer timer = new Timer(); Observable.create(subscriber

    -> { timer.onTick(() -> subscriber.onNext("tick")); }).subscribe();
  188. 441.

    @BriceDutheil @dwursteisen #DevoxxFR #Rx Timer timer = new Timer(); Observable.create(subscriber

    -> { timer.onTick(() -> subscriber.onNext("tick")); }).subscribe(); Hot
  189. 444.

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

    -> str.toLowerCase()) .subscribe(System.out::println); response .subscribe(System.out::println);
  190. 445.

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

    -> str.toLowerCase()) .subscribe(System.out::println); response .subscribe(System.out::println);
  191. 446.

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

    -> str.toLowerCase()) .subscribe(System.out::println); response .subscribe(System.out::println);
  192. 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
  193. 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
  194. 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
  195. 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);
  196. 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
  197. 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
  198. 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
  199. 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
  200. 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
  201. 458.

    @BriceDutheil @dwursteisen #DevoxxFR #Rx Il est nécessaire de remettre en

    place le contexte lors d’un changement de contexte.
  202. 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)
  203. 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
  204. 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
  205. 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
  206. 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 -> { /* ... */ });
  207. 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 ?
  208. 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
  209. 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
  210. 467.
  211. 468.

    @BriceDutheil @dwursteisen #DevoxxFR #Rx public interface WebService { Observable<String> call();

    } Va émettre une valeur ? Va émettre plusieurs valeurs ? Va émettre aucune valeur ?
  212. 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
  213. 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);
  214. 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();
  215. 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
  216. 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
  217. 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
  218. 484.

    @BriceDutheil @dwursteisen #DevoxxFR #Rx Observable.create(subscriber -> { subscriber.add(new Subscription() {

    @Override public void unsubscribe() { // fermeture du flux } }); }); Fermeture des ressources, etc
  219. 485.
  220. 487.

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

    -> Observable.just(db.doQuery()), db -> db.closeDb()) .subscribe();
  221. 488.

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

    -> Observable.just(db.doQuery()), db -> db.closeDb()) .subscribe(); Fermeture des ressources, etc
  222. 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