Slide 1

Slide 1 text

#DevoxxFR #Rx Architecture Découplée grâce aux Reactive Extensions @BriceDutheil @dwursteisen Mercredi 20 Avril - Amphi bleu - 9h30

Slide 2

Slide 2 text

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

Slide 3

Slide 3 text

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

Slide 4

Slide 4 text

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

Slide 5

Slide 5 text

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

Slide 6

Slide 6 text

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

Slide 7

Slide 7 text

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

Slide 8

Slide 8 text

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

Slide 9

Slide 9 text

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

Slide 10

Slide 10 text

@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

Slide 11

Slide 11 text

@BriceDutheil @dwursteisen #DevoxxFR #Rx Qui sommes nous ?

Slide 12

Slide 12 text

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

Slide 13

Slide 13 text

@BriceDutheil @dwursteisen #DevoxxFR #Rx Formation Conseil Consultant

Slide 14

Slide 14 text

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

Slide 15

Slide 15 text

@BriceDutheil @dwursteisen #DevoxxFR #Rx #DevoxxFR #Rx

Slide 16

Slide 16 text

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

Slide 17

Slide 17 text

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

Slide 18

Slide 18 text

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

Slide 19

Slide 19 text

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

Slide 20

Slide 20 text

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

Slide 21

Slide 21 text

@BriceDutheil @dwursteisen #DevoxxFR #Rx Le couplage temporel

Slide 22

Slide 22 text

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

Slide 23

Slide 23 text

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

Slide 24

Slide 24 text

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

Slide 25

Slide 25 text

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

Slide 26

Slide 26 text

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

Slide 27

Slide 27 text

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

Slide 28

Slide 28 text

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

Slide 29

Slide 29 text

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

Slide 30

Slide 30 text

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

Slide 31

Slide 31 text

@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 =

Slide 32

Slide 32 text

@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 =

Slide 33

Slide 33 text

@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

Slide 34

Slide 34 text

@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

Slide 35

Slide 35 text

@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

Slide 36

Slide 36 text

@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

Slide 37

Slide 37 text

@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

Slide 38

Slide 38 text

@BriceDutheil @dwursteisen #DevoxxFR #Rx Premier constat Files d’attente OS

Slide 39

Slide 39 text

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

Slide 40

Slide 40 text

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

Slide 41

Slide 41 text

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

Slide 42

Slide 42 text

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

Slide 43

Slide 43 text

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

Slide 44

Slide 44 text

@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

Slide 45

Slide 45 text

@BriceDutheil @dwursteisen #DevoxxFR #Rx Client Nouvelle requête Thread war Temps

Slide 46

Slide 46 text

@BriceDutheil @dwursteisen #DevoxxFR #Rx Client Nouvelle requête Thread war Plus de requêtes en base Plus long à répondre

Slide 47

Slide 47 text

@BriceDutheil @dwursteisen #DevoxxFR #Rx Client Nouvelle requête Thread webservice.api microservice # war Avec d’autres dépendances

Slide 48

Slide 48 text

@BriceDutheil @dwursteisen #DevoxxFR #Rx Tomcat client OS Requête # nouvelle requête Acceptor Thread Attend un slot disponible Thread war Executor full

Slide 49

Slide 49 text

@BriceDutheil @dwursteisen #DevoxxFR #Rx Point de vue du CPU Pas d’instruction à exécuter Le système attend sur des I/Os

Slide 50

Slide 50 text

@BriceDutheil @dwursteisen #DevoxxFR #Rx Deuxième observation : Couplage temporel

Slide 51

Slide 51 text

@BriceDutheil @dwursteisen #DevoxxFR #Rx On attend Services distants Base de données Fournisseurs tiers

Slide 52

Slide 52 text

@BriceDutheil @dwursteisen #DevoxxFR #Rx Localement aussi Lecture/Écriture sur disque - ex : swap Panne hardware - ex : disque en panne augmente la latence

Slide 53

Slide 53 text

@BriceDutheil @dwursteisen #DevoxxFR #Rx Solutions ?

Slide 54

Slide 54 text

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

Slide 55

Slide 55 text

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

Slide 56

Slide 56 text

@BriceDutheil @dwursteisen #DevoxxFR #Rx C’est toujours bloquant et ça peut devenir complexe

Slide 57

Slide 57 text

@BriceDutheil @dwursteisen #DevoxxFR #Rx Revient à augmenter le nombre de thread

Slide 58

Slide 58 text

@BriceDutheil @dwursteisen #DevoxxFR #Rx Sur Tomcat, utiliser un autre connecteur ? NIO, APR, NIO2 (Tomcat 8)

Slide 59

Slide 59 text

@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

Slide 60

Slide 60 text

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

Slide 61

Slide 61 text

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

Slide 62

Slide 62 text

@BriceDutheil @dwursteisen #DevoxxFR #Rx Ne pas oublier le tuning

Slide 63

Slide 63 text

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

Slide 64

Slide 64 text

@BriceDutheil @dwursteisen #DevoxxFR #Rx Comprendre la JVM

Slide 65

Slide 65 text

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

Slide 66

Slide 66 text

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

Slide 67

Slide 67 text

@BriceDutheil @dwursteisen #DevoxxFR #Rx Dans tous les cas Tomcat

Slide 68

Slide 68 text

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

Slide 69

Slide 69 text

@BriceDutheil @dwursteisen #DevoxxFR #Rx

Slide 70

Slide 70 text

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

Slide 71

Slide 71 text

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

Slide 72

Slide 72 text

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

Slide 73

Slide 73 text

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

Slide 74

Slide 74 text

@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

Slide 75

Slide 75 text

@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 ?

Slide 76

Slide 76 text

@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 ?

Slide 77

Slide 77 text

@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

Slide 78

Slide 78 text

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

Slide 79

Slide 79 text

@BriceDutheil @dwursteisen #DevoxxFR #Rx Le code pour mitiger ces problèmes est plus obscur au raisonnement

Slide 80

Slide 80 text

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

Slide 81

Slide 81 text

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

Slide 82

Slide 82 text

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

Slide 83

Slide 83 text

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

Slide 84

Slide 84 text

@BriceDutheil @dwursteisen #DevoxxFR #Rx Quelles solutions ? Tuning Tomcat Pool JDBC …

Slide 85

Slide 85 text

@BriceDutheil @dwursteisen #DevoxxFR #Rx Quelle solutions ? Changement du code

Slide 86

Slide 86 text

@BriceDutheil @dwursteisen #DevoxxFR #Rx Reactive Extensions pour mieux utiliser les ressources hardware

Slide 87

Slide 87 text

#DevoxxFR #Rx Reactive Extensions En théorie

Slide 88

Slide 88 text

@BriceDutheil @dwursteisen #DevoxxFR #Rx Historique

Slide 89

Slide 89 text

@BriceDutheil @dwursteisen #DevoxxFR #Rx Historique

Slide 90

Slide 90 text

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

Slide 91

Slide 91 text

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

Slide 92

Slide 92 text

@BriceDutheil @dwursteisen #DevoxxFR #Rx Historique

Slide 93

Slide 93 text

@BriceDutheil @dwursteisen #DevoxxFR #Rx Pourquoi faire ? Une API pour la programmation asynchrone

Slide 94

Slide 94 text

@BriceDutheil @dwursteisen #DevoxxFR #Rx Appel Synchrone - Asynchrone Appel synchrone versus Appel asynchrone

Slide 95

Slide 95 text

@BriceDutheil @dwursteisen #DevoxxFR #Rx Appel Synchrone Client Serveur

Slide 96

Slide 96 text

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

Slide 97

Slide 97 text

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

Slide 98

Slide 98 text

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

Slide 99

Slide 99 text

@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

Slide 100

Slide 100 text

@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

Slide 101

Slide 101 text

@BriceDutheil @dwursteisen #DevoxxFR #Rx Pour quoi faire ?

Slide 102

Slide 102 text

@BriceDutheil @dwursteisen #DevoxxFR #Rx Pour quoi faire ? Future ? Callback ?

Slide 103

Slide 103 text

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

Slide 104

Slide 104 text

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

Slide 105

Slide 105 text

@BriceDutheil @dwursteisen #DevoxxFR #Rx Pour quoi faire ? Future ? Callback ?

Slide 106

Slide 106 text

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

Slide 107

Slide 107 text

@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) { } }); } });

Slide 108

Slide 108 text

@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) { } }); } });

Slide 109

Slide 109 text

@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

Slide 110

Slide 110 text

@BriceDutheil @dwursteisen #DevoxxFR #Rx Pour quoi faire ? Future ? Callback ?

Slide 111

Slide 111 text

@BriceDutheil @dwursteisen #DevoxxFR #Rx Comprendre les concepts Les technologies passent. Les concepts restent.

Slide 112

Slide 112 text

@BriceDutheil @dwursteisen #DevoxxFR #Rx Concepts applicables pour d’autres bibliothèques de type Reactive Streams

Slide 113

Slide 113 text

@BriceDutheil @dwursteisen #DevoxxFR #Rx Back to basics

Slide 114

Slide 114 text

@BriceDutheil @dwursteisen #DevoxxFR #Rx RxJava permet de manipuler des évènements d’une manière synchrone et/ou asynchrone

Slide 115

Slide 115 text

@BriceDutheil @dwursteisen #DevoxxFR #Rx Flux d’évènements fini Diagramme Marble

Slide 116

Slide 116 text

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

Slide 117

Slide 117 text

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

Slide 118

Slide 118 text

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

Slide 119

Slide 119 text

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

Slide 120

Slide 120 text

@BriceDutheil @dwursteisen #DevoxxFR #Rx Quelle est la nature de ces évènements ?

Slide 121

Slide 121 text

@BriceDutheil @dwursteisen #DevoxxFR #Rx json json json Observable

Slide 122

Slide 122 text

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

Slide 123

Slide 123 text

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

Slide 124

Slide 124 text

@BriceDutheil @dwursteisen #DevoxxFR #Rx Comment écouter ces évènements ?

Slide 125

Slide 125 text

@BriceDutheil @dwursteisen #DevoxxFR #Rx Observer

Slide 126

Slide 126 text

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

Slide 127

Slide 127 text

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

Slide 128

Slide 128 text

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

Slide 129

Slide 129 text

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

Slide 130

Slide 130 text

@BriceDutheil @dwursteisen #DevoxxFR #Rx Observable.just(1, 2, 3) .subscribe(new Observer() { @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"); } });

Slide 131

Slide 131 text

@BriceDutheil @dwursteisen #DevoxxFR #Rx Observable.just(1, 2, 3) .subscribe(new Observer() { @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"); } });

Slide 132

Slide 132 text

@BriceDutheil @dwursteisen #DevoxxFR #Rx Observable.just(1, 2, 3) .subscribe(new Observer() { @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"); } });

Slide 133

Slide 133 text

@BriceDutheil @dwursteisen #DevoxxFR #Rx Observable.just(1, 2, 3) .subscribe(new Observer() { @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"); } });

Slide 134

Slide 134 text

@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") );

Slide 135

Slide 135 text

@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

Slide 136

Slide 136 text

@BriceDutheil @dwursteisen #DevoxxFR #Rx Comment manipuler ces évènements ?

Slide 137

Slide 137 text

@BriceDutheil @dwursteisen #DevoxxFR #Rx Observable

Slide 138

Slide 138 text

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

Slide 139

Slide 139 text

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

Slide 140

Slide 140 text

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

Slide 141

Slide 141 text

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

Slide 142

Slide 142 text

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

Slide 143

Slide 143 text

@BriceDutheil @dwursteisen #DevoxxFR #Rx Liste des opérateurs disponibles sur reactivex.io

Slide 144

Slide 144 text

@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 ?

Slide 145

Slide 145 text

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

Slide 146

Slide 146 text

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

Slide 147

Slide 147 text

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

Slide 148

Slide 148 text

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

Slide 149

Slide 149 text

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

Slide 150

Slide 150 text

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

Slide 151

Slide 151 text

@BriceDutheil @dwursteisen #DevoxxFR #Rx Comprendre les concepts Manipuler une monade

Slide 152

Slide 152 text

@BriceDutheil @dwursteisen #DevoxxFR #Rx Comprendre les concepts

Slide 153

Slide 153 text

@BriceDutheil @dwursteisen #DevoxxFR #Rx Concept de Monade Une structure ayant - Un type paramétré (Observable) - Une factory (Observable.just(...)) - Une méthode flatMap (obs.flatMap(...)) - Structure programmatique (Grossièrement)

Slide 154

Slide 154 text

@BriceDutheil @dwursteisen #DevoxxFR #Rx Concept de Monade Stream Java 8

Slide 155

Slide 155 text

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

Slide 156

Slide 156 text

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

Slide 157

Slide 157 text

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

Slide 158

Slide 158 text

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

Slide 159

Slide 159 text

@BriceDutheil @dwursteisen #DevoxxFR #Rx Concept de Monade Observable RxJava

Slide 160

Slide 160 text

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

Slide 161

Slide 161 text

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

Slide 162

Slide 162 text

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

Slide 163

Slide 163 text

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

Slide 164

Slide 164 text

@BriceDutheil @dwursteisen #DevoxxFR #Rx Avec les Reactive Extensions, nous allons principalement manipuler des Observables

Slide 165

Slide 165 text

@BriceDutheil @dwursteisen #DevoxxFR #Rx Structure programmatique Observable = Monade

Slide 166

Slide 166 text

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

Slide 167

Slide 167 text

@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

Slide 168

Slide 168 text

@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)

Slide 169

Slide 169 text

@BriceDutheil @dwursteisen #DevoxxFR #Rx Question : Comment créer une liste à partir d’un ensemble de valeurs ?

Slide 170

Slide 170 text

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

Slide 171

Slide 171 text

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

Slide 172

Slide 172 text

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

Slide 173

Slide 173 text

@BriceDutheil @dwursteisen #DevoxxFR #Rx Observable.just(1, 2, 3) .toList() .subscribe(result -> {/* ... */}); Effets de bord Consommation de la liste

Slide 174

Slide 174 text

@BriceDutheil @dwursteisen #DevoxxFR #Rx Question : Comment logger une opération ?

Slide 175

Slide 175 text

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

Slide 176

Slide 176 text

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

Slide 177

Slide 177 text

@BriceDutheil @dwursteisen #DevoxxFR #Rx Évitez les effets de bord (ou soyez explicite)

Slide 178

Slide 178 text

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

Slide 179

Slide 179 text

@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();

Slide 180

Slide 180 text

@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();

Slide 181

Slide 181 text

@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();

Slide 182

Slide 182 text

@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();

Slide 183

Slide 183 text

@BriceDutheil @dwursteisen #DevoxxFR #Rx Question : Comment compter le nombre d’erreur de différents Observables ?

Slide 184

Slide 184 text

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

Slide 185

Slide 185 text

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

Slide 186

Slide 186 text

@BriceDutheil @dwursteisen #DevoxxFR #Rx Question : Comment créer des Observables ?

Slide 187

Slide 187 text

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

Slide 188

Slide 188 text

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

Slide 189

Slide 189 text

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

Slide 190

Slide 190 text

@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

Slide 191

Slide 191 text

@BriceDutheil @dwursteisen #DevoxxFR #Rx Quelle méthode pour quelle utilisation ?

Slide 192

Slide 192 text

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

Slide 193

Slide 193 text

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

Slide 194

Slide 194 text

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

Slide 195

Slide 195 text

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

Slide 196

Slide 196 text

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

Slide 197

Slide 197 text

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

Slide 198

Slide 198 text

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

Slide 199

Slide 199 text

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

Slide 200

Slide 200 text

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

Slide 201

Slide 201 text

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

Slide 202

Slide 202 text

@BriceDutheil @dwursteisen #DevoxxFR #Rx Observable.defer permet de créer tardivement un Observable.

Slide 203

Slide 203 text

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

Slide 204

Slide 204 text

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

Slide 205

Slide 205 text

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

Slide 206

Slide 206 text

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

Slide 207

Slide 207 text

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

Slide 208

Slide 208 text

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

Slide 209

Slide 209 text

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

Slide 210

Slide 210 text

@BriceDutheil @dwursteisen #DevoxxFR #Rx Observable.using permet d'associer un Observable à une ressource.

Slide 211

Slide 211 text

@BriceDutheil @dwursteisen #DevoxxFR #Rx Attention Zone Dangereuse !

Slide 212

Slide 212 text

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

Slide 213

Slide 213 text

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

Slide 214

Slide 214 text

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

Slide 215

Slide 215 text

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

Slide 216

Slide 216 text

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

Slide 217

Slide 217 text

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

Slide 218

Slide 218 text

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

Slide 219

Slide 219 text

@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)

Slide 220

Slide 220 text

@BriceDutheil @dwursteisen #DevoxxFR #Rx Observable.range(1, 10) .map(i -> compute(i)) .subscribe(System.out::println); Plus simple !

Slide 221

Slide 221 text

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

Slide 222

Slide 222 text

@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

Slide 223

Slide 223 text

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

Slide 224

Slide 224 text

@BriceDutheil @dwursteisen #DevoxxFR #Rx Observable.create permet de gérer le contrat Rx et le désabonnement et la backpressure

Slide 225

Slide 225 text

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

Slide 226

Slide 226 text

#DevoxxFR #Rx Reactive Extensions En pratique

Slide 227

Slide 227 text

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

Slide 228

Slide 228 text

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

Slide 229

Slide 229 text

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

Slide 230

Slide 230 text

@BriceDutheil @dwursteisen #DevoxxFR #Rx Connaissez vous vous d’autres modèles de concurrence ? Oui Acteur, STM, Channels …

Slide 231

Slide 231 text

@BriceDutheil @dwursteisen #DevoxxFR #Rx Pour explorer d’autres modèles de concurrence

Slide 232

Slide 232 text

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

Slide 233

Slide 233 text

@BriceDutheil @dwursteisen #DevoxxFR #Rx On a vu Tomcat et les servlets OS Tomcat / BIO client Requête # Acceptor Thread Thread war

Slide 234

Slide 234 text

@BriceDutheil @dwursteisen #DevoxxFR #Rx Modèle de concurrence de RxJava

Slide 235

Slide 235 text

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

Slide 236

Slide 236 text

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

Slide 237

Slide 237 text

@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

Slide 238

Slide 238 text

@BriceDutheil @dwursteisen #DevoxxFR #Rx RxJava permet un contrôle explicite de la concurrence avec observeOn() ou avec subscribeOn()

Slide 239

Slide 239 text

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

Slide 240

Slide 240 text

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

Slide 241

Slide 241 text

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

Slide 242

Slide 242 text

@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

Slide 243

Slide 243 text

@BriceDutheil @dwursteisen #DevoxxFR #Rx observeOn() change le contexte des opérations qui suivent

Slide 244

Slide 244 text

@BriceDutheil @dwursteisen #DevoxxFR #Rx Il peut y avoir plusieurs observeOn()

Slide 245

Slide 245 text

@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

Slide 246

Slide 246 text

@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

Slide 247

Slide 247 text

@BriceDutheil @dwursteisen #DevoxxFR #Rx Certains opérateurs ont le même effet que observeOn()

Slide 248

Slide 248 text

@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

Slide 249

Slide 249 text

@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

Slide 250

Slide 250 text

@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

Slide 251

Slide 251 text

@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

Slide 252

Slide 252 text

@BriceDutheil @dwursteisen #DevoxxFR #Rx subscribeOn() définit le contexte où s’exécute la production des éléments

Slide 253

Slide 253 text

@BriceDutheil @dwursteisen #DevoxxFR #Rx Il ne peut y avoir qu’un seul subscribeOn() Premier arrivé, premier servi !

Slide 254

Slide 254 text

@BriceDutheil @dwursteisen #DevoxxFR #Rx 2 principaux modèles de schedulers

Slide 255

Slide 255 text

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

Slide 256

Slide 256 text

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

Slide 257

Slide 257 text

@BriceDutheil @dwursteisen #DevoxxFR #Rx computation() Nombre de coeur CPU .debounce(…) .filter(e -> ...) .merge(...) Élément # .filter(…) Élément <> .merge(…)

Slide 258

Slide 258 text

@BriceDutheil @dwursteisen #DevoxxFR #Rx Le modèle de computation() est une boucle d’évènements

Slide 259

Slide 259 text

@BriceDutheil @dwursteisen #DevoxxFR #Rx Augmente le rendement du CPU

Slide 260

Slide 260 text

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

Slide 261

Slide 261 text

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

Slide 262

Slide 262 text

@BriceDutheil @dwursteisen #DevoxxFR #Rx Ne PAS bloquer la boucle d’évènements

Slide 263

Slide 263 text

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

Slide 264

Slide 264 text

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

Slide 265

Slide 265 text

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

Slide 266

Slide 266 text

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

Slide 267

Slide 267 text

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

Slide 268

Slide 268 text

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

Slide 269

Slide 269 text

@BriceDutheil @dwursteisen #DevoxxFR #Rx Le modèle du scheduler io() est un pool de thread qui grossit au besoin

Slide 270

Slide 270 text

@BriceDutheil @dwursteisen #DevoxxFR #Rx io() ne bloque pas le CPU

Slide 271

Slide 271 text

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

Slide 272

Slide 272 text

@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(); } }

Slide 273

Slide 273 text

@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

Slide 274

Slide 274 text

@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); } }); } }

Slide 275

Slide 275 text

@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)

Slide 276

Slide 276 text

@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

Slide 277

Slide 277 text

@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

Slide 278

Slide 278 text

@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

Slide 279

Slide 279 text

@BriceDutheil @dwursteisen #DevoxxFR #Rx JAX-RS et Rx

Slide 280

Slide 280 text

@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) { ... } }

Slide 281

Slide 281 text

@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

Slide 282

Slide 282 text

@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

Slide 283

Slide 283 text

@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

Slide 284

Slide 284 text

@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

Slide 285

Slide 285 text

@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

Slide 286

Slide 286 text

@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

Slide 287

Slide 287 text

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

Slide 288

Slide 288 text

@BriceDutheil @dwursteisen #DevoxxFR #Rx Traitement toujours long = Attente pour le client = Toujours besoin de threads

Slide 289

Slide 289 text

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

Slide 290

Slide 290 text

@BriceDutheil @dwursteisen #DevoxxFR #Rx asyncResponse.setTimeout(50, MILLISECONDS); Retourne 503 Service Unavailable MAIS le travail en cours est toujours actif

Slide 291

Slide 291 text

@BriceDutheil @dwursteisen #DevoxxFR #Rx Introduire un TimeoutHandler Status HTTP Headers Entité personnalisée Arrêt / interruption des traitements ...

Slide 292

Slide 292 text

@BriceDutheil @dwursteisen #DevoxxFR #Rx JAX-RS apporte des fonctionnalités riches mapping, routage, filtre, …

Slide 293

Slide 293 text

@BriceDutheil @dwursteisen #DevoxxFR #Rx … mais limite l’intégration avec RxJava, par les contraintes de la spec.

Slide 294

Slide 294 text

@BriceDutheil @dwursteisen #DevoxxFR #Rx JAX-RS nous limite dans notre capacité à découpler

Slide 295

Slide 295 text

@BriceDutheil @dwursteisen #DevoxxFR #Rx Faut-il sortir de la stack JavaEE ?

Slide 296

Slide 296 text

@BriceDutheil @dwursteisen #DevoxxFR #Rx Netflix, Spotify, Twitter, Uber, d’autres sont allés dans cette direction.

Slide 297

Slide 297 text

@BriceDutheil @dwursteisen #DevoxxFR #Rx L’architecture microservices est une opportunité pour explorer les alternatives

Slide 298

Slide 298 text

@BriceDutheil @dwursteisen #DevoxxFR #Rx Par exemple en Java, Vert.x, RxNetty, …

Slide 299

Slide 299 text

@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); });

Slide 300

Slide 300 text

@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

Slide 301

Slide 301 text

@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

Slide 302

Slide 302 text

@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

Slide 303

Slide 303 text

@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

Slide 304

Slide 304 text

@BriceDutheil @dwursteisen #DevoxxFR #Rx RxNetty s’utilise pour des services très simples websocket, http, tcp, …

Slide 305

Slide 305 text

@BriceDutheil @dwursteisen #DevoxxFR #Rx RxNetty intégre RxJava au plus bas niveau

Slide 306

Slide 306 text

@BriceDutheil @dwursteisen #DevoxxFR #Rx Un main() seulement

Slide 307

Slide 307 text

@BriceDutheil @dwursteisen #DevoxxFR #Rx RxNetty offre une API de bas niveau pour plus de contrôle

Slide 308

Slide 308 text

@BriceDutheil @dwursteisen #DevoxxFR #Rx … mais demande un effort appréciable sur la mise en oeuvre des aspects d’une API HTTP publique

Slide 309

Slide 309 text

@BriceDutheil @dwursteisen #DevoxxFR #Rx Services composés

Slide 310

Slide 310 text

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

Slide 311

Slide 311 text

@BriceDutheil @dwursteisen #DevoxxFR #Rx Composite API

Slide 312

Slide 312 text

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

Slide 313

Slide 313 text

@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

Slide 314

Slide 314 text

@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

Slide 315

Slide 315 text

@BriceDutheil @dwursteisen #DevoxxFR #Rx Composite API Service A

Slide 316

Slide 316 text

@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

Slide 317

Slide 317 text

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

Slide 318

Slide 318 text

@BriceDutheil @dwursteisen #DevoxxFR #Rx @GET public void wait(@Suspended final AsyncResponse response) { Observable.fromCallable(() -> makeInterestingValue()) .flatMap(lastNumber -> { Observable aObs = Observable.fromCallable(() -> wsA(lastNumber)) .map(r -> r.readEntity(String.class)); … }) .subscribe(response::resume); } Transformation de la payload

Slide 319

Slide 319 text

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

Slide 320

Slide 320 text

@BriceDutheil @dwursteisen #DevoxxFR #Rx @GET public void wait(@Suspended final AsyncResponse response) { Observable.fromCallable(() -> makeInterestingValue()) .flatMap(lastNumber -> { Observable aObs = Observable.fromCallable(() -> wsA(lastNumber)) .map(r -> r.readEntity(String.class)); Observable bObs = Observable.fromCallable(() -> wsB(lastNumber)) .map(r -> r.readEntity(String.class)); … }) .subscribe(response::resume); } Appel observable au service B

Slide 321

Slide 321 text

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

Slide 322

Slide 322 text

@BriceDutheil @dwursteisen #DevoxxFR #Rx @GET public void wait(@Suspended final AsyncResponse response) { Observable.fromCallable(() -> makeInterestingValue()) .flatMap(lastNumber -> { Observable aObs = Observable.fromCallable(() -> wsA(lastNumber)) .map(r -> r.readEntity(String.class)); Observable 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

Slide 323

Slide 323 text

@BriceDutheil @dwursteisen #DevoxxFR #Rx Si une exception est produite ?

Slide 324

Slide 324 text

@BriceDutheil @dwursteisen #DevoxxFR #Rx @GET public void wait(@Suspended final AsyncResponse response) { Observable.fromCallable(() -> makeInterestingValue()) .flatMap(lastNumber -> { Observable aObs = Observable.fromCallable(() -> wsA(lastNumber)) .map(r -> r.readEntity(String.class)); Observable 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

Slide 325

Slide 325 text

@BriceDutheil @dwursteisen #DevoxxFR #Rx Planifier les appels externes sur Schedulers.io()

Slide 326

Slide 326 text

@BriceDutheil @dwursteisen #DevoxxFR #Rx @GET public void wait(@Suspended final AsyncResponse response) { Observable.fromCallable(() -> makeInterestingValue()) .flatMap(lastNumber -> { Observable aObs = Observable.fromCallable(() -> wsA(lastNumber)) .subscribeOn(Schedulers.io()) .map(r -> r.readEntity(String.class)); Observable 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

Slide 327

Slide 327 text

@BriceDutheil @dwursteisen #DevoxxFR #Rx Composite API Service B Service A 503

Slide 328

Slide 328 text

@BriceDutheil @dwursteisen #DevoxxFR #Rx @GET public void wait(@Suspended final AsyncResponse response) { Observable.fromCallable(() -> makeInterestingValue()) .flatMap(lastNumber -> { Observable 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 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

Slide 329

Slide 329 text

@BriceDutheil @dwursteisen #DevoxxFR #Rx @GET public void wait(@Suspended final AsyncResponse response) { Observable.fromCallable(() -> makeInterestingValue()) .flatMap(lastNumber -> { Observable 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 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

Slide 330

Slide 330 text

@BriceDutheil @dwursteisen #DevoxxFR #Rx @GET public void wait(@Suspended final AsyncResponse response) { Observable.fromCallable(() -> makeInterestingValue()) .flatMap(lastNumber -> { Observable 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 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

Slide 331

Slide 331 text

@BriceDutheil @dwursteisen #DevoxxFR #Rx @GET public void wait(@Suspended final AsyncResponse response) { Observable.fromCallable(() -> makeInterestingValue()) .flatMap(lastNumber -> { Observable 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 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

Slide 332

Slide 332 text

@BriceDutheil @dwursteisen #DevoxxFR #Rx Recommencer l’appel en cas d’erreur ?

Slide 333

Slide 333 text

@BriceDutheil @dwursteisen #DevoxxFR #Rx @GET public void wait(@Suspended final AsyncResponse response) { Observable.fromCallable(() -> makeInterestingValue()) .flatMap(lastNumber -> { Observable 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 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

Slide 334

Slide 334 text

@BriceDutheil @dwursteisen #DevoxxFR #Rx Quand une dépendance est hors-délai ?

Slide 335

Slide 335 text

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

Slide 336

Slide 336 text

@BriceDutheil @dwursteisen #DevoxxFR #Rx @GET public void wait(@Suspended final AsyncResponse response) { Observable.fromCallable(() -> makeValue()) .flatMap(lastNumber -> { Observable 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 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

Slide 337

Slide 337 text

@BriceDutheil @dwursteisen #DevoxxFR #Rx C’est beaucoup de code technique !

Slide 338

Slide 338 text

@BriceDutheil @dwursteisen #DevoxxFR #Rx Hystrix permet de résoudre ces challenges de façon plus pertinente

Slide 339

Slide 339 text

@BriceDutheil @dwursteisen #DevoxxFR #Rx @GET public void wait(@Suspended final AsyncResponse response) { Observable.fromCallable(() -> new SecureRandom().nextInt(1000)) .flatMap(lastNumber -> { Observable aObs = new ServiceACommand(lastNumber).observe(); Observable 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

Slide 340

Slide 340 text

@BriceDutheil @dwursteisen #DevoxxFR #Rx Exécution immédiate et asynchrone avec .observe() Hot

Slide 341

Slide 341 text

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

Slide 342

Slide 342 text

@BriceDutheil @dwursteisen #DevoxxFR #Rx Une commande Hystrix ne doit jamais partir en erreur

Slide 343

Slide 343 text

@BriceDutheil @dwursteisen #DevoxxFR #Rx @GET public void wait(@Suspended final AsyncResponse response) { Observable.fromCallable(() -> new SecureRandom().nextInt(1000)) .flatMap(lastNumber -> { Observable aObs = new ServiceACommand(lastNumber).observe(); Observable 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

Slide 344

Slide 344 text

@BriceDutheil @dwursteisen #DevoxxFR #Rx class ServiceACommand extends HystrixObservableCommand { 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 construct() { return Observable.fromCallable(() -> wsServiceB(number)) .map(response -> response.readEntity(String.class)); } @Override protected Observable resumeWithFallback() { return Observable.just("Service A too long"); } }

Slide 345

Slide 345 text

@BriceDutheil @dwursteisen #DevoxxFR #Rx class ServiceACommand extends HystrixObservableCommand { 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 construct() { return Observable.fromCallable(() -> wsServiceB(number)) .map(response -> response.readEntity(String.class)); } @Override protected Observable resumeWithFallback() { return Observable.just("Service A too long"); } } Commande Hystrix Type de retour

Slide 346

Slide 346 text

@BriceDutheil @dwursteisen #DevoxxFR #Rx class ServiceACommand extends HystrixObservableCommand { 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 construct() { return Observable.fromCallable(() -> wsServiceB(number)) .map(response -> response.readEntity(String.class)); } @Override protected Observable resumeWithFallback() { return Observable.just("Service A too long"); } } Commande Hystrix observable Construction

Slide 347

Slide 347 text

@BriceDutheil @dwursteisen #DevoxxFR #Rx class ServiceACommand extends HystrixObservableCommand { 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 construct() { return Observable.fromCallable(() -> wsServiceB(number)) .map(response -> response.readEntity(String.class)); } @Override protected Observable resumeWithFallback() { return Observable.just("Service A too long"); } } Appel observable

Slide 348

Slide 348 text

@BriceDutheil @dwursteisen #DevoxxFR #Rx class ServiceACommand extends HystrixObservableCommand { 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 construct() { return Observable.fromCallable(() -> wsServiceB(number)) .map(response -> response.readEntity(String.class)); } @Override protected Observable resumeWithFallback() { return Observable.just("Service A too long"); } } Plan de secours En cas d’erreur

Slide 349

Slide 349 text

@BriceDutheil @dwursteisen #DevoxxFR #Rx class ServiceACommand extends HystrixObservableCommand { 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 construct() { return Observable.fromCallable(() -> wsServiceB(number)) .map(response -> response.readEntity(String.class)); } @Override protected Observable resumeWithFallback() { return Observable.just("Service A too long"); } } Personnalisation des paramètres de la commande

Slide 350

Slide 350 text

@BriceDutheil @dwursteisen #DevoxxFR #Rx Le coupe-circuit de Hystrix

Slide 351

Slide 351 text

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

Slide 352

Slide 352 text

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

Slide 353

Slide 353 text

@BriceDutheil @dwursteisen #DevoxxFR #Rx Ouvert, la dépendance en erreur est isolée sur une période de temps

Slide 354

Slide 354 text

@BriceDutheil @dwursteisen #DevoxxFR #Rx Avertir un client lorsque le circuit est ouvert

Slide 355

Slide 355 text

@BriceDutheil @dwursteisen #DevoxxFR #Rx void executeCommand(HystrixObservableCommand 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

Slide 356

Slide 356 text

@BriceDutheil @dwursteisen #DevoxxFR #Rx void executeCommand(HystrixObservableCommand 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

Slide 357

Slide 357 text

@BriceDutheil @dwursteisen #DevoxxFR #Rx private void executeCommand(HystrixObservableCommand 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

Slide 358

Slide 358 text

@BriceDutheil @dwursteisen #DevoxxFR #Rx private void executeCommand(HystrixObservableCommand 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

Slide 359

Slide 359 text

@BriceDutheil @dwursteisen #DevoxxFR #Rx private void executeCommand(HystrixObservableCommand 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(); }); }

Slide 360

Slide 360 text

@BriceDutheil @dwursteisen #DevoxxFR #Rx Hystrix demande une certaine dose de code Il est possible d’utiliser des annotations avec hystrix-javanica

Slide 361

Slide 361 text

@BriceDutheil @dwursteisen #DevoxxFR #Rx

Slide 362

Slide 362 text

@BriceDutheil @dwursteisen #DevoxxFR #Rx Hystrix protège un système distribué de dépendances en erreur ou hors-délai

Slide 363

Slide 363 text

@BriceDutheil @dwursteisen #DevoxxFR #Rx Hystrix offre une variété d’outils

Slide 364

Slide 364 text

@BriceDutheil @dwursteisen #DevoxxFR #Rx

Slide 365

Slide 365 text

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

Slide 366

Slide 366 text

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

Slide 367

Slide 367 text

@BriceDutheil @dwursteisen #DevoxxFR #Rx Production Consommation

Slide 368

Slide 368 text

@BriceDutheil @dwursteisen #DevoxxFR #Rx Producteur Consommateur ?

Slide 369

Slide 369 text

@BriceDutheil @dwursteisen #DevoxxFR #Rx Producteur Consommateur ?

Slide 370

Slide 370 text

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

Slide 371

Slide 371 text

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

Slide 372

Slide 372 text

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

Slide 373

Slide 373 text

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

Slide 374

Slide 374 text

@BriceDutheil @dwursteisen #DevoxxFR #Rx C’est le temps d’une pause Posez nous des questions directement ou via Twitter

Slide 375

Slide 375 text

#DevoxxFR #Rx Reactive Extensions Sur notre application

Slide 376

Slide 376 text

@BriceDutheil @dwursteisen #DevoxxFR #Rx Leçons apprises en utilisant les Reactive Extensions ?

Slide 377

Slide 377 text

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

Slide 378

Slide 378 text

@BriceDutheil @dwursteisen #DevoxxFR #Rx RxJava est-il la solution à tout ? NON !

Slide 379

Slide 379 text

@BriceDutheil @dwursteisen #DevoxxFR #Rx Les Streams Java 8 peuvent être plus intéressants.

Slide 380

Slide 380 text

@BriceDutheil @dwursteisen #DevoxxFR #Rx Il est possible de faire cohabiter RxJava et Streams Java8

Slide 381

Slide 381 text

@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();

Slide 382

Slide 382 text

@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();

Slide 383

Slide 383 text

@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();

Slide 384

Slide 384 text

@BriceDutheil @dwursteisen #DevoxxFR #Rx List> 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();

Slide 385

Slide 385 text

@BriceDutheil @dwursteisen #DevoxxFR #Rx List> 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();

Slide 386

Slide 386 text

@BriceDutheil @dwursteisen #DevoxxFR #Rx List> 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();

Slide 387

Slide 387 text

@BriceDutheil @dwursteisen #DevoxxFR #Rx List> 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();

Slide 388

Slide 388 text

@BriceDutheil @dwursteisen #DevoxxFR #Rx Ce n’est pas parce que l’on peut le faire que l’on doit le faire.

Slide 389

Slide 389 text

@BriceDutheil @dwursteisen #DevoxxFR #Rx Observable 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();

Slide 390

Slide 390 text

@BriceDutheil @dwursteisen #DevoxxFR #Rx Observable 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

Slide 391

Slide 391 text

@BriceDutheil @dwursteisen #DevoxxFR #Rx Les lambdas simplifient la lecture du code

Slide 392

Slide 392 text

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

Slide 393

Slide 393 text

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

Slide 394

Slide 394 text

@BriceDutheil @dwursteisen #DevoxxFR #Rx RxView.clickEvents(tapButton) .doOnNext(event -> LOGGER.debug("--------- GOT A TAP")) .map(event -> 1) .subscribe(); 3 fois moins de code !

Slide 395

Slide 395 text

@BriceDutheil @dwursteisen #DevoxxFR #Rx Sur Android, vous pouvez utiliser Kotlin ou Retrolambda ou Java 8 (Jack&Jill)

Slide 396

Slide 396 text

@BriceDutheil @dwursteisen #DevoxxFR #Rx https://twitter.com/Piwai/status/717755657106001921

Slide 397

Slide 397 text

@BriceDutheil @dwursteisen #DevoxxFR #Rx Observable.just(1, 2, 3) .flatMap(i -> { Observable complex = webService.complexify(i) .map(str -> str.toLowerCase()) .retry(4); Observable 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();

Slide 398

Slide 398 text

@BriceDutheil @dwursteisen #DevoxxFR #Rx Observable.just(1, 2, 3) .flatMap(i -> { Observable complex = webService.complexify(i) .map(str -> str.toLowerCase()) .retry(4); Observable 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();

Slide 399

Slide 399 text

@BriceDutheil @dwursteisen #DevoxxFR #Rx Observable.just(1, 2, 3) .flatMap(i -> { Observable complex = webService.complexify(i) .map(str -> str.toLowerCase()) .retry(4); Observable 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();

Slide 400

Slide 400 text

@BriceDutheil @dwursteisen #DevoxxFR #Rx Observable.just(1, 2, 3) .flatMap(i -> { Observable complex = webService.complexify(i) .map(str -> str.toLowerCase()) .retry(4); Observable 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();

Slide 401

Slide 401 text

@BriceDutheil @dwursteisen #DevoxxFR #Rx Observable.just(1, 2, 3) .flatMap(i -> { Observable complex = webService.complexify(i) .map(str -> str.toLowerCase()) .retry(4); Observable 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();

Slide 402

Slide 402 text

@BriceDutheil @dwursteisen #DevoxxFR #Rx Observable.just(1, 2, 3) .flatMap(i -> { Observable complex = webService.complexify(i) .map(str -> str.toLowerCase()) .retry(4); Observable 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 !

Slide 403

Slide 403 text

@BriceDutheil @dwursteisen #DevoxxFR #Rx Observable.just(1, 2, 3) .flatMap(i -> { Observable complex = webService.complexify(i) .map(str -> str.toLowerCase()) .retry(4); Observable 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

Slide 404

Slide 404 text

@BriceDutheil @dwursteisen #DevoxxFR #Rx Observable.just(1, 2, 3) .flatMap(i -> transform(i)) .subscribe(); public Observable transform(int i) { Observable complex = webService.complexify(i) .map(str -> str.toLowerCase()) .retry(4); Observable 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); }

Slide 405

Slide 405 text

@BriceDutheil @dwursteisen #DevoxxFR #Rx Observable.just(1, 2, 3) .flatMap(i -> transform(i)) .subscribe(); public Observable transform(int i) { Observable complex = fetchComplexify(i); Observable human = fetchHuman(i); return Observable.zip(complex, human, (c, h) -> c + h) .flatMap(str -> webService.frequencyOf(str)) .flatMapIterable(itr -> itr) }

Slide 406

Slide 406 text

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

Slide 407

Slide 407 text

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

Slide 408

Slide 408 text

@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é

Slide 409

Slide 409 text

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

Slide 410

Slide 410 text

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

Slide 411

Slide 411 text

@BriceDutheil @dwursteisen #DevoxxFR #Rx flatMap permet d'émettre zéro, un, ou plusieurs évènements

Slide 412

Slide 412 text

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

Slide 413

Slide 413 text

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

Slide 414

Slide 414 text

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

Slide 415

Slide 415 text

@BriceDutheil @dwursteisen #DevoxxFR #Rx flatMap ne remplace pas un Observable par un autre.

Slide 416

Slide 416 text

@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 !"));

Slide 417

Slide 417 text

@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 !"));

Slide 418

Slide 418 text

@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 !"));

Slide 419

Slide 419 text

@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 !"));

Slide 420

Slide 420 text

@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 !"));

Slide 421

Slide 421 text

@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 !

Slide 422

Slide 422 text

@BriceDutheil @dwursteisen #DevoxxFR #Rx Il faut comprendre le contrat Rx pour comprendre les opérateurs

Slide 423

Slide 423 text

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

Slide 424

Slide 424 text

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

Slide 425

Slide 425 text

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

Slide 426

Slide 426 text

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

Slide 427

Slide 427 text

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

Slide 428

Slide 428 text

@BriceDutheil @dwursteisen #DevoxxFR #Rx Observable.interval

Slide 429

Slide 429 text

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

Slide 430

Slide 430 text

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

Slide 431

Slide 431 text

@BriceDutheil @dwursteisen #DevoxxFR #Rx Hot versus Cold

Slide 432

Slide 432 text

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

Slide 433

Slide 433 text

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

Slide 434

Slide 434 text

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

Slide 435

Slide 435 text

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

Slide 436

Slide 436 text

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

Slide 437

Slide 437 text

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

Slide 438

Slide 438 text

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

Slide 439

Slide 439 text

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

Slide 440

Slide 440 text

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

Slide 441

Slide 441 text

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

Slide 442

Slide 442 text

@BriceDutheil @dwursteisen #DevoxxFR #Rx Observable response = ws.call();

Slide 443

Slide 443 text

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

Slide 444

Slide 444 text

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

Slide 445

Slide 445 text

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

Slide 446

Slide 446 text

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

Slide 447

Slide 447 text

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

Slide 448

Slide 448 text

@BriceDutheil @dwursteisen #DevoxxFR #Rx Observable 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

Slide 449

Slide 449 text

@BriceDutheil @dwursteisen #DevoxxFR #Rx Observable 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

Slide 450

Slide 450 text

@BriceDutheil @dwursteisen #DevoxxFR #Rx Utilisation de MDC pour les logs : attention aux mauvaises surprises.

Slide 451

Slide 451 text

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

Slide 452

Slide 452 text

@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);

Slide 453

Slide 453 text

@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

Slide 454

Slide 454 text

@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

Slide 455

Slide 455 text

@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

Slide 456

Slide 456 text

@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

Slide 457

Slide 457 text

@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

Slide 458

Slide 458 text

@BriceDutheil @dwursteisen #DevoxxFR #Rx Il est nécessaire de remettre en place le contexte lors d’un changement de contexte.

Slide 459

Slide 459 text

@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)

Slide 460

Slide 460 text

@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

Slide 461

Slide 461 text

@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

Slide 462

Slide 462 text

@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

Slide 463

Slide 463 text

@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 -> { /* ... */ });

Slide 464

Slide 464 text

@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 ?

Slide 465

Slide 465 text

@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

Slide 466

Slide 466 text

@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

Slide 467

Slide 467 text

@BriceDutheil @dwursteisen #DevoxxFR #Rx Grâce à OnErrorThrowable, il est possible d'accéder à la valeur responsable de l’exception

Slide 468

Slide 468 text

@BriceDutheil @dwursteisen #DevoxxFR #Rx public interface WebService { Observable call(); } Va émettre une valeur ? Va émettre plusieurs valeurs ? Va émettre aucune valeur ?

Slide 469

Slide 469 text

@BriceDutheil @dwursteisen #DevoxxFR #Rx Single = onSuccess | onError (Actuellement en @Beta)

Slide 470

Slide 470 text

@BriceDutheil @dwursteisen #DevoxxFR #Rx Completable = onCompleted | onError (Actuellement en @Experimental)

Slide 471

Slide 471 text

@BriceDutheil @dwursteisen #DevoxxFR #Rx public interface WebService { Observable list(); Single get(); Completable perform(); } Va émettre un ensemble de valeurs va émettre une unique valeur ne va pas émettre de valeur

Slide 472

Slide 472 text

@BriceDutheil @dwursteisen #DevoxxFR #Rx Single et Completable permettent d’exprimer une intention.

Slide 473

Slide 473 text

@BriceDutheil @dwursteisen #DevoxxFR #Rx La souscription permet de signaler le désabonnement à un flux

Slide 474

Slide 474 text

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

Slide 475

Slide 475 text

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

Slide 476

Slide 476 text

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

Slide 477

Slide 477 text

@BriceDutheil @dwursteisen #DevoxxFR #Rx CompositeSubscription permet de gérer un ensemble d’abonnements

Slide 478

Slide 478 text

@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);

Slide 479

Slide 479 text

@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();

Slide 480

Slide 480 text

@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

Slide 481

Slide 481 text

@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

Slide 482

Slide 482 text

@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

Slide 483

Slide 483 text

@BriceDutheil @dwursteisen #DevoxxFR #Rx Du code peut être exécuté lors du désabonnement

Slide 484

Slide 484 text

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

Slide 485

Slide 485 text

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

Slide 486

Slide 486 text

@BriceDutheil @dwursteisen #DevoxxFR #Rx Mais comment le faire sans utiliser Observable.create ?

Slide 487

Slide 487 text

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

Slide 488

Slide 488 text

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

Slide 489

Slide 489 text

@BriceDutheil @dwursteisen #DevoxxFR #Rx Avant de se dire au revoir

Slide 490

Slide 490 text

@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

Slide 491

Slide 491 text

@BriceDutheil @dwursteisen #DevoxxFR #Rx Remerciements Gautier Mechling @Nilhcem Simon Baslé @simonbasle David Karnok @akarnokd Dan Lew @danlew42

Slide 492

Slide 492 text

@BriceDutheil @dwursteisen #DevoxxFR #Rx Retrouvez nous dans les couloirs de Devoxx

Slide 493

Slide 493 text

@BriceDutheil @dwursteisen #DevoxxFR #Rx Y a t’il des points que nous n’aurions pas abordés ?

Slide 494

Slide 494 text

@BriceDutheil @dwursteisen #DevoxxFR #Rx Merci

Slide 495

Slide 495 text

@BriceDutheil @dwursteisen #DevoxxFR #Rx

Slide 496

Slide 496 text

@BriceDutheil @dwursteisen #DevoxxFR #Rx