Reactor 3 par l’exemple – Fais chauffer le barbuc !!

On va explorer ensemble la bibliothèque Reactor 3. J’ai envie de faire un peu de veille sur cette lib’. Du coup, je ne suis dit que l’on va la tester ensemble, toi et moi, entre gens bons.

Comme d’ab, je commence par tester le code en premier, lire la doc en second. Je ne suis pas sur de te montrer des bonnes pratiques, après tout, je fais ce post pour découvrir comment marche cette bibliothèque. Si tu vois des conneries, n’hésite pas à faire des retours.

Reactor 3, c’est quoi ?

Reactor 3 est une bibliothèque de programmation réactive que l’on retrouve dans Spring 5, papati papata …

Mais, attends !! Tu es là pour voir du code, je te laisse lire le site officiel du projet pour plus de théorie. J’ai pu voir cette conférence, cette vidéo explique beaucoup mieux que moi qu’est ce que Reactor 3.

Du code, rien que du code !!

Je voulais du code explicite pour illustrer mes exemples. Du coup, je suis parti sur un exemple d’application qui gère un barbecue (ou barbuc pour les intimes). Et je m’excuse d’avance pour mes amis végétariens, mais il y aura que de la viande à griller. Tous les exemples suivants se trouvent dans ce repository.

 

 

 

 

 

Partie 1: Vous n’avez pas faim ?

Exemple 1-1: Mon premier flux

On va commencer simple et créer un flux. Les classes “Flux” et “Mono” font parties des classes de base de Reactor 3. Elles sont des Publishers.
On va créer un flux de 3 saucisses, avec la fonction “just”.

//1-1 Un flux de 3 saussisses dans une assiette
public static void main(String[] args) {
	BBQHeap plate = new BBQHeap();
	Flux<Sausage> sausageFlux = Flux.just(new Sausage(), new Sausage(), new Sausage());
	sausageFlux.subscribe(sausage -> {
		plate.add(sausage);
		System.out.println(plate.toString());
	});
}

La fonction “subscribe” permet de lancer le Publisher. On met en paramètre un lamda Consumer, il va se transformer en Subscriber. Les Subscribers permettent de définir les actions faites sur les éléments d’un Publisher.

Voila, on lance le test, ça marche, on a un flux de 3 saucisses que l’on met dans une assiette, qui nous sert de “tas à viande” cuite.

Exemple 1-2: Bien cuite, ta saucisse ?

Bon, on va se dire qu’il faut une seconde pour faire une saucisse sur le barbuc (tu peux mettre une durée plus “normale”, comme 5 minutes, si tu as du temps à perdre …) et que notre barbuc peut cuire qu’une saucisse à la fois. (oui, ce n’est pas très efficace, on va améliorer dans les exemples suivants)

On va utiliser un flux “interval” infini qui se déclenche sur une fréquence fixe définie. Du coup, je le “zip” avec mon flux de saucisse pour rajouter mon temps de cuisson. J’ai trouvé un excellent article qui explique la différence entre concat, merge et zip. Ainsi, toutes les secondes, on a une saucisse qui sort.

//1-2 Flux Barbuk, toutes les X secondes/minutes, on rajoute 1 saussisse dans une assiette, 3 fois
public static void main(String[] args) throws InterruptedException {
	BBQHeap plate = new BBQHeap();
	Flux<Sausage> sausageFlux = Flux.just(new Sausage(), new Sausage(), new Sausage());
	Flux<Long> sausageInterval = Flux.interval(Duration.ofSeconds(1));
	Flux.zip(sausageFlux, sausageInterval, (eachSausage, eachInterval) -> eachSausage)
		.subscribe(sausage -> {
			plate.add(sausage);
			System.out.println(plate.toString());
		});
	Thread.sleep(3000);
}

“Thread.sleep” vient d’apparaître en fin de programme de 3 secondes, puisque le flux s’exécute dans un autre thread parallèle. On est obligé de faire attendre le thread principal si on veut voir le résultat.

Exemple 1-3: Ramène ta bouffe …

Cool, j’attends 3 secondes la fin de mon programme. Maintenant, on gère un flux, on ne sait pas combien d’élément que l’on va cuire et on veut attendre tant que ce n’est pas fini.

BBQHeap plate = new BBQHeap();
Flux<Sausage> sausageFlux = Flux.just(new Sausage()).repeat(5 + (int) (Math.random() * 10));
Flux<Long> sausageInterval = Flux.interval(Duration.ofMillis(1000));

Disposable disposable = Flux.zip(sausageFlux, sausageInterval, (eachSausage, eachInterval) -> eachSausage)
	.subscribe(sausage -> {
		plate.add(sausage);
		System.out.println(plate.toString());
});

while (!disposable.isDisposed()) {
	Thread.sleep(1000);
}

On va utiliser l’objet Disposable, retourné par la fonction subscribe. La fonction isDisposed de cet objet reste à faux tant que le flux n’est pas terminé.

Exemple 1-4: Moi, j’aime pô les saucisses

Bon, maintenant, on va cuire aussi des steaks. On peut cuire une saucisse en même temps qu’un steak, le steak met plus de temps pour cuire, environ 5 fois plus (à la louche), notre barbuc peut cuire qu’un steak à la fois.

//1-4 On rajoute les steak, il mette plus de temps pour cuire
BBQHeap plate = new BBQHeap();

Flux<Sausage> sausageFlux = Flux.just(new Sausage()).repeat(30 + (int) (Math.random() * 20));
Flux<Long> sausageInterval = Flux.interval(Duration.ofMillis(1000));
sausageFlux = Flux.zip(sausageFlux, sausageInterval, (eachSausage, eachInterval) -> eachSausage);

Flux<Steak> steakFlux = Flux.just(new Steak()).repeat(10 + (int) (Math.random() * 5));
Flux<Long> steakInterval = Flux.interval(Duration.ofMillis(5000));
steakFlux = Flux.zip(steakFlux, steakInterval, (eachSteak, eachInterval) -> eachSteak);

Disposable disposable = Flux.merge(sausageFlux, steakFlux).subscribe(sausage -> {
		plate.add(sausage);
		System.out.println(plate.toString());
});

while (!disposable.isDisposed()) {
	Thread.sleep(1000);
}
			

Du coup, on va créer 2 flux que l’on va merger. La cuisson de nos deux viandes se cuisent en même temps, chacun a son rythme.

Exemple 1-5: Tu es sur que c’est bien cuit ?

Bon, on va tester l’algorythme de l’exemple précédent, on va tester le flux. Du coup, regardons la classe de test BBQAppExample1_5_Test.

private BBQAppExample1_5 example = new BBQAppExample1_5();

@Test
public void test1() {
	Duration testDuration = StepVerifier.create(example.cook(8,1))
		.assertNext(barbecuable -> Assertions.assertThat(barbecuable).isExactlyInstanceOf(Sausage.class))
    	.thenCancel()
    	.verify();
        System.out.println(testDuration.toMillis() + " ms");
}

@Test
public void test2() {
	StepVerifier.create(example.cook(8,1))
    	.assertNext(barbecuable -> Assertions.assertThat(barbecuable).isExactlyInstanceOf(Sausage.class))
        .expectNextCount(4)
        .assertNext(barbecuable -> Assertions.assertThat(barbecuable).isExactlyInstanceOf(Steak.class))
        .assertNext(barbecuable -> Assertions.assertThat(barbecuable).isExactlyInstanceOf(Sausage.class))
        .expectNextCount(2)
        .expectComplete()
        .verify();
}

On va utiliser la bibliothèque de test lié à Reactor 3. StepVerifier permet de mettre en place des tests. Les tests marchent par “step”, chaque étape du flux peut être vérifié. On a mis en place deux tests: “test1” et “test2” (bouh le nommage !!)

“test1” vérifie si le premier élément est une saucisse, l’élément le plus rapide à cuire. Du coup, on passe par la fonction assertNext, qui permet de tester une condition pour le prochain élément de notre flux. La bibliothèque AssertJ s’intègre très bien avec Reactor test. Je stoppe les étapes de test, je ne vais pas à la fin du flux. On finit avec la fonction verify des que le test est terminé. On peut aussi tester la durée du test, afin de mettre en place des tests de performance.

“test2” permet de vérifier le flux en entier, dans la même idée que “test1”, mais en plus compliqué.

Partie 2: Un peu de rab’

Je suis parti dans un autre exemple. J’ai pensé aux potes qui font la queue devant le tas de viande cuite afin d’insérer leurs deux chipos trop cuites dans leur pains coupés en deux.

Exemple 2-1: Mange ta saussisse

Du coup, on essaye un Mono et on mange la saussisse présente dans l’assiette. On passe aussi par la fonction “doOnNext”, un moyen différent de parcourir un mono ou un flux. On joue aussi un peu avec les Optionals de Java 8. Si on trouve une saucisse dans l’assiette, on la prend.

Exemple 2-2: T’as pas faim ?

Bon, on rajoute la fonctionnalité “faim”. Avec la fonction “repeat”, Pierre continue à manger tant qu’il a encore faim.

Exemple 2-3: Et Si tu as encore faim, je ramène deux, trois copains !!

Bon, on rajoute d’autres personnes pour manger. Avec la fonction “repeat” du flux, tant que tout le monde n’a pas fini de manger, on continue de piocher dans l’assiette.

Parti 3: C’est la merguez, merguez party !!

Du coup, maintenant que je commence à voir comment cela se passe avec les flux de Reactor 3, je change mon barbecue (partie 1). Je me suis dit, un exemple compliqué pour embrouiller tout le monde, cela manquait. Si tu n’as rien compris au flux avec les exemples précédents, ça n’va pas t’aider.

On crée un flux continu tout le long de mon objet BBQ. Tout le long de ce flux, on enlève les grillades cuites ou on rajoute des viandes si il y a de la place.

Effet Bernard Minet,  j’ai un peu craqué. J’ai rajouté aussi d’autres fonctionnalités:

– Les différentes cuissons: Les viandes peuvent être cuites avec différentes cuissons. Le steak cuit plus longtemps que les saucisses.

– le cuisto: Il s’occupe du barbecue. C’est lui qui est responsable du barbecue, quand êtes ce qu’il faut rajouter de la viande et quand est ce qu’il faut l’arrêter. Le cuisto a une fréquence de check du barbuc. (s’il préfère boire une bière avec les autres plutôt que de surveiller la cuisson au millimètre) J’utilise la puissance des lambdas pour faire évoluer les envies de cuisson de notre cuisto. Par exemple: Avec le temps, il en a marre et préfère discuter avec les autres autour de la piscine, et du coup, il fait cuire la viande moins forte et peut quitter son poste plus rapidement.

– Je rajoute aussi des paramètres pour la création du barbecue: On peut ainsi adapter son nombre maximum de viande et sa fréquence de cuisson (qui permet de jouer sur la puissance de ce barbuc).

Bon, je m’arrête là. Il faudra lier tout cela, nos mangeurs avec la cuisson de notre barbecue afin de finir l’application. On en garde un peu pour un prochain sujet.

Conclusion: Tant qu’il y a des braises, c’est pas fini !!

Bon, voici plusieurs exemples de code Reactor. Cette bibliothèque est présent nativement dans Spring Boot 2. Rhaa, il faut tester ça !! Peut être dans un nouveau post …

1 thought on “Reactor 3 par l’exemple – Fais chauffer le barbuc !!”

  1. Pingback: Webservice Reactor 3 – La pause Dev'

Leave a Reply

Your email address will not be published. Required fields are marked *