SlideShare une entreprise Scribd logo
1  sur  64
Télécharger pour lire hors ligne
Programmation
Réactive
Fonctionnelle
Sébastien Chèvre – 28.10.2016
Au menu
Concepts
Besoins
Push vs Pull
RxJava
reactive-streams
Concepts - Définition
«… la programmation réactive est un
paradigme de programmation visant à
conserver une cohérence d'ensemble en
propageant les modifications d'une source
réactive (modification d'une variable, entrée
utilisateur, etc.) aux éléments dépendants de
cette source…»
Concepts - Définition
Programmation Réactive
==
programmation avec des flux de données
asynchrones
Réactif
• flux de données
• propagation des changements
Fonctionnel
• décompositions fonctionnelles
• pas d’effets de bord
Concepts - Flux de données asynchrone
• Séquence d’événements
• Ordonnés dans le temps
• Non concurents
• 0 à N événements
• Flux fini/infini/vide
Concepts - Il était une fois le Pattern Observer…
Besoins – Gestion efficiente des ressources
Besoins - Manifeste Réactif / Systèmes réactifs
Besoins - résumé
• Les besoins des applications croient plus
vite que les évolutions matérielles
• Il est temps de repenser l’architecture
logicielle et les techniques de
programmation
Besoins - résumé
• Les besoins des applications croient plus
vite que les évolutions matérielles
• Il est temps de repenser l’architecture
logicielle et les techniques de
programmation
Liste des personnes de sexe féminin
Push vs Pull
public List<Personne> getSexeFeminin (List<Personne> personnes) {
List<Personne> sexeFeminin = new ArrayList<>();
for(Personne personne: personnes){
if(personne.isFromSexe(Sexe.FEMININ)){
sexeFeminin.add(personne);
}
}
return sexeFeminin;
}
Liste des personnes de sexe féminin
Push vs Pull
public List<Personne> getSexeFeminin(List<Personne> personnes) {
return personnes.stream()
.filter(personne -> personne.isFromSexe(Sexe.FEMININ))
.collect(Collectors.toList());
}
Liste des personnes de sexe féminin
Push vs Pull
public Observable<Personne> getSexeFeminin (List<Personne> personnes){
return Observable.from(personnes)
.filter(personne-> personne.isFromSexe(Sexe.FEMININ));
}
Push vs Pull
Iterable – Iterator
Observable – Observer
Evénement Iterable (Pull) Observable(Push)
Obtenir une valeur T next(); onNext(T);
Remontées des
erreurs
throws Exception(); onError(Throwable);
Complete !hasNext(); onCompleted();
Asynchronisme - Future
Bien faire du code asynchrone ?
Future
Se composent mal
Peuvent bloquer (get())
Valeurs scalaires
Gestion des erreurs complexe
Asynchronisme - Callback
Bien faire du code asynchrone ?
Callback
Simple
Pas de blocage
Ne se composent pas
Difficilement lisible après 1 niveau (callback hell)
RxJava
RxJava
RxJava
Reactive Extensions
• Développé par Microsoft (2009!)
• Portages divers (RX.js, RX.java)
• Rxjava -> NETFLIX
• Librairie Open source
• Communauté active
• Version 1.2.1
• 2.0-RC3
RxJava - Building blocks
Subscriber<T>Subject<T>
Observer<T>
Scheduler<T>
Observable<T> Subscription<T>
Operator<T>
Producer
//just prend en paramètre un à n objet à emettre
Observable<String> justObs = Observable.just("un","deux","trois");
//from prend en paramètre une instance d'Iterable, un Future ou un
tableau et émet ses éléments
Observable<BigDecimal> fromObs = Observable.from(
Arrays.asList(BigDecimal.ONE, BigDecimal.TEN,BigDecimal.ZERO)
);
//intervall génère une valeur depuis 0, à chaque intervalle spécifé
Observable<Long> intervalObs = Observable.interval(100,10,
TimeUnit.MILLISECONDS);
RxJava - Observable
2 manière d’instancier un observable:
• Via les constructeurs statiques:
RxJava - Observable
• via la méthode create, en implémentant l’interface
OnSubscribe:
Observable<String> prenomsObservable = Observable.create(new
Observable.OnSubscribe<String>() {
@Override
public void call(Subscriber<? super String> subscriber) {
try{
for(String prenom : prenoms){
subscriber.onNext(prenom);
}
subscriber.onCompleted();
}catch(Exception ex){
subscriber.onError(ex.getCause());
}
}
});
RxJava - Observable
• via la méthode create, en implémentant l’interface
OnSubscribe:
Observable<String> prenomsObservable = Observable.create(subscriber
-> {
try{
for(String prenom : prenoms){
subscriber.onNext(prenom);
}
subscriber.onCompleted();
}catch(Exception ex){
subscriber.onError(ex.getCause());
}
});
RxJava - Observer
public interface Observer<T> {
void onCompleted();
void onError(Throwable throwable);
void onNext(T next);
}
Contrat RX
onNext : chaque fois qu’un événement est émis
onError : appelé quand une exception survient
onCompleted : fin du flux
RxJava - Subscription
public interface Subscription {
void unsubscribe();
boolean isUnsubscribed();
}
• Interface permettant de gérer la souscription
• Observable.subscribe() retourne une Subscription
• Représente la lien entre Observable et Observer
RxJava - Premier Observable
Observable<String> firstObservable = Observable.create(new
Observable.OnSubscribe<String>(){
@Override
public void call(Subscriber<? super String> subscriber){
subscriber.onNext("Hello");
subscriber.onNext("World");
subscriber.onCompleted();
}
});
RxJava - Premier Subscriber
Subscriber<String> firstSubscriber = new Subscriber<String>() {
@Override
public void onNext(String s){
System.out.println("Next: " + s);
}
@Override
public void onCompleted(){
System.out.println("Complete!");
}
@Override
public void onError(Throwable throwable){
System.out.println(throwable.getCause().getMessage();
}
RxJava - Mode java 8, tout ensemble
Observable.create(subscriber -> {
subscriber.onNext("Hello");
subscriber.onNext("World");
subscriber.onCompleted();
})
.subscribe(
next -> System.out.println(next),
error -> System.out.println(error.getCause().getMessage()),
() -> System.out.println("Complete")
);
//Encore plus court…
Observable.from(Arrays.asList("Hello","World"))
.subscribe(
System.out::println,
System.out::println,
() -> System.out.println("Complete")
);
RxJava - Opérateurs
• Plus d’une centaine d’opérateurs
• Implémentation d’opérateurs « custom »
• Filtre, map, reduce, fusion, etc
• Immuable : traite un Observable et retourne un Observable
• Diagramme marble
RxJava - Opérateurs
RxJava - Opérateurs
RxJava - Opérateurs
RxJava - Opérateurs
RxJava - Penser en terme de flux
Prénoms des personnes de sexes féminins en
majuscule
Observable<User> usersObservable = Observable.from(personnes);
usersObservable
.filter(personne -> {
return personne.isFromSexe(Sexe.FEMININ);
})
.map(personne -> {
return personne.prenom().toUpperCase();
})
.subscribe(value->{
System.out.println(value);
});
filter (x –> x.isSexeFrom(Sexe.FEMININ))
map (x –> x.prenom.toUpperCase)
subscribe
Observable.from(personnes)
onNext onNext
onNext
onNext
onNext onNext
onNext
onNext
Completed
Completed
Completed
RxJava - Penser en terme de flux
RxJava - Penser en terme de flux, filtre
Observable<User> usersObservable = Observable.from(personnes);
usersObservable
.filter(personne -> {
return personne.isFromSexe(Sexe.FEMININ);
})
.map(personne -> {
return personne.prenom().toUpperCase();
})
.first()
.subscribe(value->{
System.out.println(value);
});
Premier prénom des personnes de sexes féminins
en majuscule
first()
filter (x –> x.isSexeFrom(Sexe.FEMININ))
map (x –> x.prenom.toUpperCase)
subscribe
Observable.from(personnes)
onCompleted
onNext onNext
onNext
onNext
onNext
RxJava - Penser en terme de flux, filtre
filter (x –> x.isSexeFrom(Sexe.FEMININ))
map (x –> x.prenom.toUpperCase)
subscribe
Observable.from(personnes)
onNext onNext
onNext
onNext
onNext onNext
onNext
onError
RxJava - Penser en terme de flux, erreurs
RxJava - Gestion des erreurs
Principes de bases
• Aucune exception levé hors de l’Observable
• CompositeException
• Encapsule plusieurs exception
• OnErrorNotImplementedException
• si onError() pas implémenté
• OnErrorFailedException
• observer.onError() soulève une exception
RxJava - Gestion des erreurs
Principes avancés
• OnErrorResumeNext
Observable
.from(Personne.getPersonnesWithNullPointerPrenom())
.filter(personne -> personne.nom().startsWith("C"))
.onErrorResumeNext((error) -> {
return Observable.from(Personne.getPersonnes());
})
.subscribe(
System.out::println,
System.out::println,
() -> System.out.println("Complete")
);
RxJava - Gestion des erreurs
Principes avancés
Observable
.from(Personne.getPersonnesWithNullPointerPrenom())
.filter(personne -> personne.nom().startsWith("C"))
.onErrorReturn((error) -> {
return Personne.getPersonnes().get(1);
})
.subscribe(
System.out::println,
System.out::println,
() -> System.out.println("Complete")
);
• OnErrorReturn
RxJava - Gestion des erreurs
Principes avancés
Observable
.from(Personne.getPersonnesWithNullPointerPrenom())
.filter(personne -> personne.nom().startsWith("C"))
.retry(1)
.subscribe(
System.out::println,
System.out::println,
() -> System.out.println("Complete")
);
• Retry
Multithreading - Schedulers
Schedulers Description
Schedulers.computation() Pour calcul long. NThread = NCpu
Schedulers.io() Pool de thread interne. Pour gestion I/O
Schedulers.from(executor) Utilisation d’un Executor
Schedulers.newThread() Un thread pour chaque unité de travail
• Fourni une abstraction pour la gestion de la concurrence
• Fourni différentes stratégies (Thread pools, Event Loops,
Handler, etc…)
• Sert à contrôler l’exécution des traitements
• Associé aux opérateurs subscribeOn et observeOn
Multithreading - subscribeOn()
• Indique à l’Observable source sur quel Thread émettre les
événements
• L’ordre d’apparition dans le pipeline n’a aucune importance
• Ou sont émit les événements
Multithreading - Par défaut : synchrone
Observable.from(Arrays.asList(1,2,3,4,5,6))
.filter(integer -> integer % 2 == 0)
.map(integer -> "Pair:" + integer)
.subscribe(intStr -> {
printThreadNameAnd(intStr);
sleep(100);
});
[main] - Pair:2
[main] - Pair:4
[main] - Pair:6
Multithreading - Par défaut : synchrone
Observable.from(Arrays.asList(1,2,3,4,5,6))
.subscribeOn(Scheduler.io())
.filter(integer -> integer % 2 == 0)
.map(integer -> "Pair:" + integer)
.subscribe(intStr -> {
printThreadNameAnd(intStr);
sleep(100);
});
……..
Multithreading - Par défaut : synchrone
printThreadNameAnd("Starting...");
Observable.from(Arrays.asList(1,2,3,4,5,6))
.subscribeOn(Scheduler.io())
.filter(integer -> integer % 2 == 0)
.map(integer -> "Pair:" + integer)
.subscribe(intStr -> {
printThreadNameAnd(intStr);
sleep(100);
});
sleep(1000);
printThreadNameAnd("End");
[main:1475491635202] - Starting...
[RxCachedThreadScheduler-1:1475491635570] - Pair:2
[RxCachedThreadScheduler-1:1475491635671] - Pair:4
[RxCachedThreadScheduler-1:1475491635771] - Pair:6
[main:1475491636572] - End
Multithreading - observeOn()
• Indique aux l’opérateur situé
juste après sur quel Thread
déporter leurs traitements
• Ou sont consommées
les événements
Multithreading - observeOn()
Observable<Integer> obs = Observable.range(1,20);
Obs
.filter(integer -> integer % 2 == 0)
.observeOn(Schedulers.computation())
.map(integer -> "Pair:" + integer)
.subscribe(intStr -> {
printThreadNameAnd("Next subscriber: " +intStr);
sleep(100);
});
;
source ObserveOn
map
Subscriber
filter
Parallélisation
Observable<Integer> intObs = Observable.range(1,10);
intObs
.map(val -> traitementLong(val))
.subscribe(val -> System.out.println(val));
public static Integer traitementLong(Integer i) {
try {
threadLogAnd("Calcul i: " + i);
Thread.sleep(1500);
return i;
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
Parallélisation
Observable<Integer> intObs = Observable.range(1,10);
intObs
.flatMap(val ->
Observable.just(val)
.subscribeOn(Schedulers.computation())
.map(i -> traitementLong(i))
)
.subscribe(val -> System.out.println(val));
public static Integer traitementLong(Integer i) {
try {
threadLogAnd("Calcul i: " + i);
Thread.sleep(1500);
return i;
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
Parallélisation
flatMap ()
subscribeOn
map()
onNext [main]
subscribeOn
map()
onNext
onNext
onNext [main]
subscribeOn
map()
subscribeOn
map()
onNext
onNext
subscribe
onNext [main] onNext [main]
onNext
onNext
onNext
onNext
onNext onNextonNext onNext
Observable.interval(1, TimeUnit.MILLISECONDS)
.map(String::valueOf)
.subscribe(
next -> {
logThread(next);
sleep(1000);
},
throwable -> {
logThread(throwable.getMessage());
},
() ->{
logThread("Complete");
});
Backpressure – single thread synchrone
subscribe
map (x –> new TestObj(x))
onNext
onNext
Observable.interval(1, TimeUnit.MILLISECONDS)
.map(String::valueOf)
.subscribe(
next -> {
logThread(next);
sleep(1000);
},
throwable -> {
logThread(throwable.getMessage());
},
() ->{
logThread("Complete");
});
Backpressure – single thread synchrone
subscribe
map (x –> new TestObj(x))
onNext
onNext
Backpressure – multithread
Observable.interval(1, TimeUnit.MILLISECONDS)
.observeOn(Schedulers.computation())
.map(String::valueOf)
.subscribe(
next -> {
logThread(next);
sleep(1000);
},
throwable -> {
logThread(throwable.getMessage());
},
() ->{
logThread("Complete");
});
source
map
subscriber
ObserveOn
MissingBackpressureException
Backpressure – échantillonage
Observable.interval(1, TimeUnit.MILLISECONDS)
.observeOn(Schedulers.computation())
.sample(1000, TimeUnits.MILLISECONDS)
.map(String::valueOf)
.subscribe(
next -> {
logThread(next);
sleep(1000);
},
throwable -> {
logThread(throwable.getMessage());
},
() ->{
logThread("Complete");
});
Backpressure – solutions bufferisation
Observable.interval(1, TimeUnit.MILLISECONDS)
.observeOn(Schedulers.computation())
.buffer(100, TimeUnits.MILLISECONDS)
.flatMap(ids -> Observable.from(ids).map(String::valueOf))
.subscribe(
next -> {
logThread(next);
sleep(1000);
},
throwable -> {
logThread(throwable.getMessage());
},
() ->{
logThread("Complete");
});
Backpressure – reactive pull
• Via une implémentation de l’interface Producer, et de la méthode
request(long n)
• Permet le contrôle de l’émission de l’Observable
• L’Observable doit supporter la backpressure
.subscribe(new Subscriber<Integer>() {
@Override
public void onStart() {
request(1);
}
@Override
public void onNext(Integer item) {
System.out.println("Next: " + item);
request(1);
}
…
RxJava - Debugg et tests
Debugg
Méthodes doOnXXX, à effet de bords
• doOnNext
• doOnError
• doOnCompleted
• …
RxJava - Debugg et tests
Debugg
Méthodes doOnXXX, à effet de bords
• doOnNext
• doOnError
• doOnCompleted
• …
Observable.from(Arrays.asList(1,2,3,4,5,6,7))
.doOnNext(valeur -> System.out.println("Next from: " + valeur))
.doOnCompleted(()->System.out.println("Complete from"))
.map(valeur -> valeur + 5)
.doOnNext(valeur -> System.out.println("Next map: " + valeur))
.doOnCompleted(()->System.out.println("Complete map"))
.filter(valeur -> valeur % 2 == 0)
.doOnNext(valeur -> System.out.println("Next filter: " + valeur))
.doOnCompleted(()->System.out.println("Complete filter"))
.map(valeur -> valeur -2 )
.subscribe(valeur -> System.out.println("Next subscribe: " + valeur));
RxJava - Debugg et tests
Tests
• Observable  BlockingObservable
• Observer  TestObserver
//Test de l‘Observable
BlockingObservable<Response<Integer>> obsBlock = obs.toBlocking();
Integer dernier = (Integer) obsBlock.last();
Response<Integer> resp = obsBlock.first();
assertThat(resp.isSuccess()).isFalse();
//Test de l’observer
TestSubscriber testSub = new TestSubscriber();
obs.subscribe(testSub);
testSub.assertNoErrors();
List<Integer> ints = testSub.getOnNextEvents();
reactive-streams.org
• Standard pour la gestion des flux asynchrones
• JVM, Javascript
• Version 1.0.0 actuelle
Implémentations actuelles
• RxJavaReactiveStreams
• RxJava 2.x
• Projet Reactor
• Vert.x
• Akka Streams
• Slick (mapping relationnel réactif pour Scala)
reactive-streams.org
Rx java softshake 2016

Contenu connexe

Similaire à Rx java softshake 2016

Apache Storm - Introduction au traitement temps-réel avec Storm
Apache Storm - Introduction au traitement temps-réel avec StormApache Storm - Introduction au traitement temps-réel avec Storm
Apache Storm - Introduction au traitement temps-réel avec StormParis_Storm_UG
 
Paris stormusergroup intrudocution
Paris stormusergroup intrudocutionParis stormusergroup intrudocution
Paris stormusergroup intrudocutionParis_Storm_UG
 
Java 8 - collections et stream
Java 8 - collections et streamJava 8 - collections et stream
Java 8 - collections et streamFranck SIMON
 
Algorithme distribués pour big data saison 2 @DevoxxFR 2016
Algorithme distribués pour big data saison 2 @DevoxxFR 2016Algorithme distribués pour big data saison 2 @DevoxxFR 2016
Algorithme distribués pour big data saison 2 @DevoxxFR 2016Duyhai Doan
 
WS User Group - Spring Batch - Xebia
WS User Group - Spring Batch - XebiaWS User Group - Spring Batch - Xebia
WS User Group - Spring Batch - XebiaOlivier BAZOUD
 
react-slides.ppx (2) (1).pptx react presentation basic
react-slides.ppx (2) (1).pptx react presentation basicreact-slides.ppx (2) (1).pptx react presentation basic
react-slides.ppx (2) (1).pptx react presentation basiczineblahib2
 
[Devoxx MA 2023] R2DBC = R2D2 + JDBC (enfin presque...)
[Devoxx MA 2023] R2DBC = R2D2 + JDBC (enfin presque...)[Devoxx MA 2023] R2DBC = R2D2 + JDBC (enfin presque...)
[Devoxx MA 2023] R2DBC = R2D2 + JDBC (enfin presque...)Bruno Bonnin
 
Programmes et objets informatiques (Prof. Ch. Rapin, Juillet 1989)
Programmes et objets informatiques (Prof. Ch. Rapin, Juillet 1989)Programmes et objets informatiques (Prof. Ch. Rapin, Juillet 1989)
Programmes et objets informatiques (Prof. Ch. Rapin, Juillet 1989)ebruchez
 

Similaire à Rx java softshake 2016 (14)

Apache Storm - Introduction au traitement temps-réel avec Storm
Apache Storm - Introduction au traitement temps-réel avec StormApache Storm - Introduction au traitement temps-réel avec Storm
Apache Storm - Introduction au traitement temps-réel avec Storm
 
Paris stormusergroup intrudocution
Paris stormusergroup intrudocutionParis stormusergroup intrudocution
Paris stormusergroup intrudocution
 
Présentation nouveauté java7
Présentation nouveauté java7Présentation nouveauté java7
Présentation nouveauté java7
 
Java 8 - collections et stream
Java 8 - collections et streamJava 8 - collections et stream
Java 8 - collections et stream
 
Introduction à React
Introduction à ReactIntroduction à React
Introduction à React
 
Algorithme distribués pour big data saison 2 @DevoxxFR 2016
Algorithme distribués pour big data saison 2 @DevoxxFR 2016Algorithme distribués pour big data saison 2 @DevoxxFR 2016
Algorithme distribués pour big data saison 2 @DevoxxFR 2016
 
C# 7 - Nouveautés
C# 7 - NouveautésC# 7 - Nouveautés
C# 7 - Nouveautés
 
Lustre
LustreLustre
Lustre
 
WS User Group - Spring Batch - Xebia
WS User Group - Spring Batch - XebiaWS User Group - Spring Batch - Xebia
WS User Group - Spring Batch - Xebia
 
Kotlin coroutines
Kotlin coroutinesKotlin coroutines
Kotlin coroutines
 
react-slides.ppx (2) (1).pptx react presentation basic
react-slides.ppx (2) (1).pptx react presentation basicreact-slides.ppx (2) (1).pptx react presentation basic
react-slides.ppx (2) (1).pptx react presentation basic
 
[Devoxx MA 2023] R2DBC = R2D2 + JDBC (enfin presque...)
[Devoxx MA 2023] R2DBC = R2D2 + JDBC (enfin presque...)[Devoxx MA 2023] R2DBC = R2D2 + JDBC (enfin presque...)
[Devoxx MA 2023] R2DBC = R2D2 + JDBC (enfin presque...)
 
Atelier gwt
Atelier gwtAtelier gwt
Atelier gwt
 
Programmes et objets informatiques (Prof. Ch. Rapin, Juillet 1989)
Programmes et objets informatiques (Prof. Ch. Rapin, Juillet 1989)Programmes et objets informatiques (Prof. Ch. Rapin, Juillet 1989)
Programmes et objets informatiques (Prof. Ch. Rapin, Juillet 1989)
 

Rx java softshake 2016

  • 2. Au menu Concepts Besoins Push vs Pull RxJava reactive-streams
  • 3. Concepts - Définition «… la programmation réactive est un paradigme de programmation visant à conserver une cohérence d'ensemble en propageant les modifications d'une source réactive (modification d'une variable, entrée utilisateur, etc.) aux éléments dépendants de cette source…»
  • 4.
  • 5. Concepts - Définition Programmation Réactive == programmation avec des flux de données asynchrones Réactif • flux de données • propagation des changements Fonctionnel • décompositions fonctionnelles • pas d’effets de bord
  • 6. Concepts - Flux de données asynchrone • Séquence d’événements • Ordonnés dans le temps • Non concurents • 0 à N événements • Flux fini/infini/vide
  • 7. Concepts - Il était une fois le Pattern Observer…
  • 8. Besoins – Gestion efficiente des ressources
  • 9. Besoins - Manifeste Réactif / Systèmes réactifs
  • 10. Besoins - résumé • Les besoins des applications croient plus vite que les évolutions matérielles • Il est temps de repenser l’architecture logicielle et les techniques de programmation
  • 11. Besoins - résumé • Les besoins des applications croient plus vite que les évolutions matérielles • Il est temps de repenser l’architecture logicielle et les techniques de programmation
  • 12. Liste des personnes de sexe féminin Push vs Pull public List<Personne> getSexeFeminin (List<Personne> personnes) { List<Personne> sexeFeminin = new ArrayList<>(); for(Personne personne: personnes){ if(personne.isFromSexe(Sexe.FEMININ)){ sexeFeminin.add(personne); } } return sexeFeminin; }
  • 13. Liste des personnes de sexe féminin Push vs Pull public List<Personne> getSexeFeminin(List<Personne> personnes) { return personnes.stream() .filter(personne -> personne.isFromSexe(Sexe.FEMININ)) .collect(Collectors.toList()); }
  • 14. Liste des personnes de sexe féminin Push vs Pull public Observable<Personne> getSexeFeminin (List<Personne> personnes){ return Observable.from(personnes) .filter(personne-> personne.isFromSexe(Sexe.FEMININ)); }
  • 15. Push vs Pull Iterable – Iterator Observable – Observer Evénement Iterable (Pull) Observable(Push) Obtenir une valeur T next(); onNext(T); Remontées des erreurs throws Exception(); onError(Throwable); Complete !hasNext(); onCompleted();
  • 16. Asynchronisme - Future Bien faire du code asynchrone ? Future Se composent mal Peuvent bloquer (get()) Valeurs scalaires Gestion des erreurs complexe
  • 17. Asynchronisme - Callback Bien faire du code asynchrone ? Callback Simple Pas de blocage Ne se composent pas Difficilement lisible après 1 niveau (callback hell)
  • 19. RxJava Reactive Extensions • Développé par Microsoft (2009!) • Portages divers (RX.js, RX.java) • Rxjava -> NETFLIX • Librairie Open source • Communauté active • Version 1.2.1 • 2.0-RC3
  • 20. RxJava - Building blocks Subscriber<T>Subject<T> Observer<T> Scheduler<T> Observable<T> Subscription<T> Operator<T> Producer
  • 21. //just prend en paramètre un à n objet à emettre Observable<String> justObs = Observable.just("un","deux","trois"); //from prend en paramètre une instance d'Iterable, un Future ou un tableau et émet ses éléments Observable<BigDecimal> fromObs = Observable.from( Arrays.asList(BigDecimal.ONE, BigDecimal.TEN,BigDecimal.ZERO) ); //intervall génère une valeur depuis 0, à chaque intervalle spécifé Observable<Long> intervalObs = Observable.interval(100,10, TimeUnit.MILLISECONDS); RxJava - Observable 2 manière d’instancier un observable: • Via les constructeurs statiques:
  • 22. RxJava - Observable • via la méthode create, en implémentant l’interface OnSubscribe: Observable<String> prenomsObservable = Observable.create(new Observable.OnSubscribe<String>() { @Override public void call(Subscriber<? super String> subscriber) { try{ for(String prenom : prenoms){ subscriber.onNext(prenom); } subscriber.onCompleted(); }catch(Exception ex){ subscriber.onError(ex.getCause()); } } });
  • 23. RxJava - Observable • via la méthode create, en implémentant l’interface OnSubscribe: Observable<String> prenomsObservable = Observable.create(subscriber -> { try{ for(String prenom : prenoms){ subscriber.onNext(prenom); } subscriber.onCompleted(); }catch(Exception ex){ subscriber.onError(ex.getCause()); } });
  • 24. RxJava - Observer public interface Observer<T> { void onCompleted(); void onError(Throwable throwable); void onNext(T next); } Contrat RX onNext : chaque fois qu’un événement est émis onError : appelé quand une exception survient onCompleted : fin du flux
  • 25. RxJava - Subscription public interface Subscription { void unsubscribe(); boolean isUnsubscribed(); } • Interface permettant de gérer la souscription • Observable.subscribe() retourne une Subscription • Représente la lien entre Observable et Observer
  • 26. RxJava - Premier Observable Observable<String> firstObservable = Observable.create(new Observable.OnSubscribe<String>(){ @Override public void call(Subscriber<? super String> subscriber){ subscriber.onNext("Hello"); subscriber.onNext("World"); subscriber.onCompleted(); } });
  • 27. RxJava - Premier Subscriber Subscriber<String> firstSubscriber = new Subscriber<String>() { @Override public void onNext(String s){ System.out.println("Next: " + s); } @Override public void onCompleted(){ System.out.println("Complete!"); } @Override public void onError(Throwable throwable){ System.out.println(throwable.getCause().getMessage(); }
  • 28. RxJava - Mode java 8, tout ensemble Observable.create(subscriber -> { subscriber.onNext("Hello"); subscriber.onNext("World"); subscriber.onCompleted(); }) .subscribe( next -> System.out.println(next), error -> System.out.println(error.getCause().getMessage()), () -> System.out.println("Complete") ); //Encore plus court… Observable.from(Arrays.asList("Hello","World")) .subscribe( System.out::println, System.out::println, () -> System.out.println("Complete") );
  • 29. RxJava - Opérateurs • Plus d’une centaine d’opérateurs • Implémentation d’opérateurs « custom » • Filtre, map, reduce, fusion, etc • Immuable : traite un Observable et retourne un Observable • Diagramme marble
  • 34. RxJava - Penser en terme de flux Prénoms des personnes de sexes féminins en majuscule Observable<User> usersObservable = Observable.from(personnes); usersObservable .filter(personne -> { return personne.isFromSexe(Sexe.FEMININ); }) .map(personne -> { return personne.prenom().toUpperCase(); }) .subscribe(value->{ System.out.println(value); });
  • 35. filter (x –> x.isSexeFrom(Sexe.FEMININ)) map (x –> x.prenom.toUpperCase) subscribe Observable.from(personnes) onNext onNext onNext onNext onNext onNext onNext onNext Completed Completed Completed RxJava - Penser en terme de flux
  • 36. RxJava - Penser en terme de flux, filtre Observable<User> usersObservable = Observable.from(personnes); usersObservable .filter(personne -> { return personne.isFromSexe(Sexe.FEMININ); }) .map(personne -> { return personne.prenom().toUpperCase(); }) .first() .subscribe(value->{ System.out.println(value); }); Premier prénom des personnes de sexes féminins en majuscule
  • 37. first() filter (x –> x.isSexeFrom(Sexe.FEMININ)) map (x –> x.prenom.toUpperCase) subscribe Observable.from(personnes) onCompleted onNext onNext onNext onNext onNext RxJava - Penser en terme de flux, filtre
  • 38. filter (x –> x.isSexeFrom(Sexe.FEMININ)) map (x –> x.prenom.toUpperCase) subscribe Observable.from(personnes) onNext onNext onNext onNext onNext onNext onNext onError RxJava - Penser en terme de flux, erreurs
  • 39. RxJava - Gestion des erreurs Principes de bases • Aucune exception levé hors de l’Observable • CompositeException • Encapsule plusieurs exception • OnErrorNotImplementedException • si onError() pas implémenté • OnErrorFailedException • observer.onError() soulève une exception
  • 40. RxJava - Gestion des erreurs Principes avancés • OnErrorResumeNext Observable .from(Personne.getPersonnesWithNullPointerPrenom()) .filter(personne -> personne.nom().startsWith("C")) .onErrorResumeNext((error) -> { return Observable.from(Personne.getPersonnes()); }) .subscribe( System.out::println, System.out::println, () -> System.out.println("Complete") );
  • 41. RxJava - Gestion des erreurs Principes avancés Observable .from(Personne.getPersonnesWithNullPointerPrenom()) .filter(personne -> personne.nom().startsWith("C")) .onErrorReturn((error) -> { return Personne.getPersonnes().get(1); }) .subscribe( System.out::println, System.out::println, () -> System.out.println("Complete") ); • OnErrorReturn
  • 42. RxJava - Gestion des erreurs Principes avancés Observable .from(Personne.getPersonnesWithNullPointerPrenom()) .filter(personne -> personne.nom().startsWith("C")) .retry(1) .subscribe( System.out::println, System.out::println, () -> System.out.println("Complete") ); • Retry
  • 43. Multithreading - Schedulers Schedulers Description Schedulers.computation() Pour calcul long. NThread = NCpu Schedulers.io() Pool de thread interne. Pour gestion I/O Schedulers.from(executor) Utilisation d’un Executor Schedulers.newThread() Un thread pour chaque unité de travail • Fourni une abstraction pour la gestion de la concurrence • Fourni différentes stratégies (Thread pools, Event Loops, Handler, etc…) • Sert à contrôler l’exécution des traitements • Associé aux opérateurs subscribeOn et observeOn
  • 44. Multithreading - subscribeOn() • Indique à l’Observable source sur quel Thread émettre les événements • L’ordre d’apparition dans le pipeline n’a aucune importance • Ou sont émit les événements
  • 45. Multithreading - Par défaut : synchrone Observable.from(Arrays.asList(1,2,3,4,5,6)) .filter(integer -> integer % 2 == 0) .map(integer -> "Pair:" + integer) .subscribe(intStr -> { printThreadNameAnd(intStr); sleep(100); }); [main] - Pair:2 [main] - Pair:4 [main] - Pair:6
  • 46. Multithreading - Par défaut : synchrone Observable.from(Arrays.asList(1,2,3,4,5,6)) .subscribeOn(Scheduler.io()) .filter(integer -> integer % 2 == 0) .map(integer -> "Pair:" + integer) .subscribe(intStr -> { printThreadNameAnd(intStr); sleep(100); }); ……..
  • 47. Multithreading - Par défaut : synchrone printThreadNameAnd("Starting..."); Observable.from(Arrays.asList(1,2,3,4,5,6)) .subscribeOn(Scheduler.io()) .filter(integer -> integer % 2 == 0) .map(integer -> "Pair:" + integer) .subscribe(intStr -> { printThreadNameAnd(intStr); sleep(100); }); sleep(1000); printThreadNameAnd("End"); [main:1475491635202] - Starting... [RxCachedThreadScheduler-1:1475491635570] - Pair:2 [RxCachedThreadScheduler-1:1475491635671] - Pair:4 [RxCachedThreadScheduler-1:1475491635771] - Pair:6 [main:1475491636572] - End
  • 48. Multithreading - observeOn() • Indique aux l’opérateur situé juste après sur quel Thread déporter leurs traitements • Ou sont consommées les événements
  • 49. Multithreading - observeOn() Observable<Integer> obs = Observable.range(1,20); Obs .filter(integer -> integer % 2 == 0) .observeOn(Schedulers.computation()) .map(integer -> "Pair:" + integer) .subscribe(intStr -> { printThreadNameAnd("Next subscriber: " +intStr); sleep(100); }); ; source ObserveOn map Subscriber filter
  • 50. Parallélisation Observable<Integer> intObs = Observable.range(1,10); intObs .map(val -> traitementLong(val)) .subscribe(val -> System.out.println(val)); public static Integer traitementLong(Integer i) { try { threadLogAnd("Calcul i: " + i); Thread.sleep(1500); return i; } catch (InterruptedException e) { throw new RuntimeException(e); } }
  • 51. Parallélisation Observable<Integer> intObs = Observable.range(1,10); intObs .flatMap(val -> Observable.just(val) .subscribeOn(Schedulers.computation()) .map(i -> traitementLong(i)) ) .subscribe(val -> System.out.println(val)); public static Integer traitementLong(Integer i) { try { threadLogAnd("Calcul i: " + i); Thread.sleep(1500); return i; } catch (InterruptedException e) { throw new RuntimeException(e); } }
  • 52. Parallélisation flatMap () subscribeOn map() onNext [main] subscribeOn map() onNext onNext onNext [main] subscribeOn map() subscribeOn map() onNext onNext subscribe onNext [main] onNext [main] onNext onNext onNext onNext onNext onNextonNext onNext
  • 53. Observable.interval(1, TimeUnit.MILLISECONDS) .map(String::valueOf) .subscribe( next -> { logThread(next); sleep(1000); }, throwable -> { logThread(throwable.getMessage()); }, () ->{ logThread("Complete"); }); Backpressure – single thread synchrone subscribe map (x –> new TestObj(x)) onNext onNext
  • 54. Observable.interval(1, TimeUnit.MILLISECONDS) .map(String::valueOf) .subscribe( next -> { logThread(next); sleep(1000); }, throwable -> { logThread(throwable.getMessage()); }, () ->{ logThread("Complete"); }); Backpressure – single thread synchrone subscribe map (x –> new TestObj(x)) onNext onNext
  • 55. Backpressure – multithread Observable.interval(1, TimeUnit.MILLISECONDS) .observeOn(Schedulers.computation()) .map(String::valueOf) .subscribe( next -> { logThread(next); sleep(1000); }, throwable -> { logThread(throwable.getMessage()); }, () ->{ logThread("Complete"); }); source map subscriber ObserveOn MissingBackpressureException
  • 56. Backpressure – échantillonage Observable.interval(1, TimeUnit.MILLISECONDS) .observeOn(Schedulers.computation()) .sample(1000, TimeUnits.MILLISECONDS) .map(String::valueOf) .subscribe( next -> { logThread(next); sleep(1000); }, throwable -> { logThread(throwable.getMessage()); }, () ->{ logThread("Complete"); });
  • 57. Backpressure – solutions bufferisation Observable.interval(1, TimeUnit.MILLISECONDS) .observeOn(Schedulers.computation()) .buffer(100, TimeUnits.MILLISECONDS) .flatMap(ids -> Observable.from(ids).map(String::valueOf)) .subscribe( next -> { logThread(next); sleep(1000); }, throwable -> { logThread(throwable.getMessage()); }, () ->{ logThread("Complete"); });
  • 58. Backpressure – reactive pull • Via une implémentation de l’interface Producer, et de la méthode request(long n) • Permet le contrôle de l’émission de l’Observable • L’Observable doit supporter la backpressure .subscribe(new Subscriber<Integer>() { @Override public void onStart() { request(1); } @Override public void onNext(Integer item) { System.out.println("Next: " + item); request(1); } …
  • 59. RxJava - Debugg et tests Debugg Méthodes doOnXXX, à effet de bords • doOnNext • doOnError • doOnCompleted • …
  • 60. RxJava - Debugg et tests Debugg Méthodes doOnXXX, à effet de bords • doOnNext • doOnError • doOnCompleted • … Observable.from(Arrays.asList(1,2,3,4,5,6,7)) .doOnNext(valeur -> System.out.println("Next from: " + valeur)) .doOnCompleted(()->System.out.println("Complete from")) .map(valeur -> valeur + 5) .doOnNext(valeur -> System.out.println("Next map: " + valeur)) .doOnCompleted(()->System.out.println("Complete map")) .filter(valeur -> valeur % 2 == 0) .doOnNext(valeur -> System.out.println("Next filter: " + valeur)) .doOnCompleted(()->System.out.println("Complete filter")) .map(valeur -> valeur -2 ) .subscribe(valeur -> System.out.println("Next subscribe: " + valeur));
  • 61. RxJava - Debugg et tests Tests • Observable  BlockingObservable • Observer  TestObserver //Test de l‘Observable BlockingObservable<Response<Integer>> obsBlock = obs.toBlocking(); Integer dernier = (Integer) obsBlock.last(); Response<Integer> resp = obsBlock.first(); assertThat(resp.isSuccess()).isFalse(); //Test de l’observer TestSubscriber testSub = new TestSubscriber(); obs.subscribe(testSub); testSub.assertNoErrors(); List<Integer> ints = testSub.getOnNextEvents();
  • 62. reactive-streams.org • Standard pour la gestion des flux asynchrones • JVM, Javascript • Version 1.0.0 actuelle Implémentations actuelles • RxJavaReactiveStreams • RxJava 2.x • Projet Reactor • Vert.x • Akka Streams • Slick (mapping relationnel réactif pour Scala)