Blog ENI : Toute la veille numérique !
🐠 -25€ dès 75€ 
+ 7 jours d'accès à la Bibliothèque Numérique ENI. Cliquez ici
Accès illimité 24h/24 à tous nos livres & vidéos ! 
Découvrez la Bibliothèque Numérique ENI. Cliquez ici
  1. Livres et vidéos
  2. Java Spring
  3. Introduction à Spring Reactor et Spring Webflux
Extrait - Java Spring Le socle technique des applications Jakarta EE (4e édition)
Extraits du livre
Java Spring Le socle technique des applications Jakarta EE (4e édition) Revenir à la page d'achat du livre

Introduction à Spring Reactor et Spring Webflux

Introduction

Ce chapitre présente brièvement les nouveautés réactives de Spring 5. Les applications réactives avec Spring sont traitées plus en détail dans l’ouvrage "Java Spring - Construisez vos applications réactives avec une architecture micro-services en environnement Java EE" aux Éditions ENI.

L’objectif de la programmation réactive est de simplifier l’écriture d’applications asynchrones qui, au lieu d’utiliser le mécanisme classique d’un volumineux pool de threads bloquants, utilise une boucle et un nombre minimum de threads grâce à un moteur basé sur un framework NIO comme Netty.

Nous plaçons dans une file d’attente des traitements rapides à effectuer, et ils se font au fil de l’eau. Il devient alors possible de modéliser ces traitements sous la forme de flux et de propagations de changements. Nous pouvons gérer des flux statiques avec des tableaux ou dynamiques avec des émetteurs et récepteurs d’événements comme dans le design pattern observateur.

Dans un contexte de serveur d’un cluster élastique, ce type de traitement "sériel" permet de savoir si la charge sur un serveur est en adéquation avec la puissance du cluster via la backpressure qui mesure le volume du flux acceptable en entrée...

Spring Reactor

1. Présentation

Historiquement nous avions RxJava 1 et 2 pour faire de la programmation réactive. 

Spring a décidé d’implémenter Reactor, sa propre version de 4e génération d’un moteur réactif afin de pouvoir utiliser au mieux les technologies actuelles.

Reactor est particulièrement bien adapté à Spring. Il peut facilement s’interfacer avec RxJava et avec l’équivalent du JDK9 : java.util.concurrent.Flow.

Une application réactive est caractérisée par ces points :

  • Réactif : fournit des temps de réponse rapides et cohérents.

  • Résilient : reste réactif en cas de panne et doit récupérer.

  • Élastique : reste réactif et est capable de gérer diverses charges de travail.

  • Message Driven : communique à l’aide de messages asynchrones.

Les applications réactives ne sont pas plus rapides que les applications traditionnelles. Elles ont par contre un comportement beaucoup mieux maîtrisé et beaucoup plus prévisible quand le serveur vient à saturer. Bien que l’application Java soit saturée, le système hôte reste parfaitement accessible. Les applications sont plus intuitives à programmer et mieux structurées grâce à la programmation fonctionnelle.

Nous avons d’un côté le Publisher (producteur) qui produit un ou plusieurs éléments qui sont ensuite consommés par un Consumer (consommateur). Il faut considérer que ces éléments échangés sont des événements.

Il est à noter que la programmation est asynchrone. Le code n’est plus exécuté de façon séquentielle. Deux traitements de flux qui se succèdent dans le code sont lancés en parallèle. Nous n’attendons pas la fin du traitement d’un flux pour passer au flux suivant. Paradoxalement, le système ne lance qu’un traitement à la fois dans sa file d’attente. Il faudra donc synchroniser les flux.

Il existe deux types de Publisher :

  • Celui qui émet 0 ou 1 élément :

    Le Mono : reactor.core.publisher.Mono<T>

  • Celui qui émet de 0 à N éléments :

    Le Flux : reactor.core.publisher.Flux<T>...

WebFlux

Nous utiliserons généralement Reactor dans le cadre d’applications WebFlux.

En effet les API Reactive sur des flux d’événements statiques sont souvent remplaçables par les Streams traditionnels. WebFlux est l’équivalent réactif de Spring MVC et utilise le moteur Reactor en interne.

Concevoir de zéro une application WebFlux est complexe. Depuis peu, JHipster permet de générer des applications complètes l’utilisant, et son utilisation peut faire gagner beaucoup de temps.

Pour utiliser Spring WebFlux, il faut ajouter la dépendance Maven :

<dependency> 
   <groupId>org.springframework.boot</groupId> 
   <artifactId>spring-boot-starter-webflux</artifactId> 
   <version>2.6.7</version> 
</dependency> 

Un exemple complet généré avec JHipster est disponible dans les exemples téléchargeables de l’ouvrage. Les détails le concernant sont étudiés dans un chapitre dédié.

1. Définition du terme réactif

Les notions de réactivités dans WebFlux sont de plusieurs ordres. Plutôt que d’allouer ou associer via un pool un thread à une requête qui gère l’intégralité du traitement d’une requête jusqu’à sa réponse, nous découpons le traitement en petits éléments qui se placent dans une file d’attente et s’exécutent au plus tôt.

Nous traitons cet élément dans un environnement asynchrone non bloquant.

Dans un serveur Spring MVC, on traite les requêtes tant qu’il y a des threads disponibles. Quand ces derniers viennent à manquer, on fait attendre le client HTTP. Le client attend une connexion disponible.

Dans un serveur Spring WebFlux, on ne bloque pas les clients. S’il survient trop d’appels, on sature également le système mais à un autre endroit, au niveau de la boucle d’événements.

La différence se situe principalement dans le fait que nous n’avons pas de threads bloqués qui attendent le résultat d’une étape un peu longue.

Nous pourrions croire que nous avons l’équivalent en utilisant...

Client réactif

Nous pouvons utiliser la classe WebClient qui a été introduite dans Spring 5 et qui est l’équivalent du RestTemplate traditionnel. C’est un client non bloquant prenant en charge les flux réactifs. Il permet de récupérer les données des endpoints fournis par le contrôleur WebFlux. La classe WebClient va remplacer le RestTemplate qui va devenir déprécié sous peu.

Créons un simple UtilisateurWebClient:

public class UtilisateurWebClient { 
 
   WebClient client = WebClient.create("http://localhost:8080"); 
 
   // ... 
} 

Récupération d’une seule ressource :

Mono<Utilisateur> utilisateurMono = client.get() 
 .uri("/utilisateurs/{id}", "1") 
 .retrieve() 
 .bodyToMono(Utilisateur.class); 
 
utilisateurMono.subscribe(System.out::println); 

Récupération d’une collection :

Flux<Utilisateur> utilisateursFlux = client.get() 
 .uri("/utilisateurs") 
 .retrieve() 
 .bodyToFlux(Utilisateur.class); 
employeeFlux.subscribe(System.out::println); 

Les tests avec WebFlux

Spring a mis à disposition un certain nombre d’outils pour faciliter les tests.

1. Tests unitaires

Les test unitaires correspondent aux tests de bas niveau.

a. Tests unitaires avec des applications réactives

Nous pouvons utiliser le traditionnel Mockito.

Pour une classe :

public class UtilisateurService { 
 public Mono<Utilisateur> getUtilisateurById(Integer utilisateurId) { 
 return webClient 
   .get() 
   .uri("http://localhost:8080/utilisateur/{id}", utilisateurId) 
   .retrieve() 
   .bodyToMono(Utilisateur.class); 
 } 
} 

Nous pouvons avoir le test unitaire :

@ExtendWith(MockitoExtension.class) 
public class UtilisateurServiceTest { 
   
 @Test 
 void givenUtilisateurId_whenGetUtilisateurById_thenReturnUtilisateur() { 
 
 Integer utilisateurId = 100; 
 Utilisateur mockUtilisateur = new Utilisateur(1, "Toto", 51); 
 when(webClientMock.get()) 
   .thenReturn(requestHeadersUriSpecMock); 
 when(requestHeadersUriMock.uri("/utilisateur/{id}", utilisateurId)) 
   .thenReturn(requestHeadersSpecMock); 
 when(requestHeadersMock.retrieve()) 
   .thenReturn(responseSpecMock); 
 when(responseMock.bodyToMono(Utilisateur.class)) 
   .thenReturn(Mono.just(mockUtilisateur)); 
 
 Mono<Utilisateur> utilisateurMono = 
utilisateurService.getUtilisateurById(utilisateurId); 
 
 StepVerifier.create(utilisateurMono) 
   .expectNextMatches(utilisateur -> utilisateur.getNom() 
   .equals("Toto")) 
   .verifyComplete(); 
 } 
} 

b. Utilisation de MockWebServer

MockWebServer a été mis au point par l’équipe Squaere dont vous retrouverez le travail sur le site : https://github.com/square/okhttp/tree/master/mockwebserver

Le MockWebServer est un serveur web scriptable permettant de tester les clients HTTP...

Server Site Event avec Spring

Il faut souvent, dans nos applications, faire des rafraîchissements pour des appels asynchrones. Il existe pour cela plusieurs technologies. La plus simple est le pooling qui consiste à interroger régulièrement le serveur pour connaître l’état d’avancement d’un traitement. Une autre évolution est le long pooling qui consiste à garder ouvert la connexion HTTP mais ces deux techniques sont dispendieuses au niveau du réseau. Une évolution existe avec les webhooks et le Publish-Subscribe. Nous avons ensuite les websockets qui sont bidirectionnelles. Nous avons aussi le Server Site Event, ou SSE en abrégé, qui est une norme HTTP permettant à une application web de gérer un flux d’événements unidirectionnel et de recevoir des mises à jour chaque fois que le serveur émet des données. Cette solution est souvent la mieux adaptée dans un contexte web qui ne requiert pas l’aspect temps réel des websockets. Nous sommes alors en léger différé mais cela est parfois soutenable.

La version Spring 4.2 supportait déjà le SSE, mais, à partir de Spring 5, il existe un moyen plus idiomatique et pratique de le gérer via un flux et un élément ServerSiteEvent grâce aux API réactives de Spring WebFlux. On crée...

Pour aller plus loin

L’adoption des applications réactives est très lente. Ce sont a priori les mêmes freins que pour l’utilisation du Domain Driven Design, des architectures hexagonales, de l’Event Sourcing etc. Pour le moment il est souvent jugé plus simple de travailler de façon traditionnelle, même si cela implique plus de serveurs. 

Spring met à disposition un ensemble complet, fonctionnant avec Java et Kotlin. Les serveurs les plus innovants sont en Kotlin, utilisent les API réactives et fonctionnent avec des Flux dans le Cloud. Les applications réactives utilisent souvent des bases Kafka et Cassandra et sont basées sur des événements.

Le nombre de serveurs devient un critère important quand on utilise un Cloud Amazon, Google, Azure ou autre car on cherche à réduire le nombre de machines. 

Points clés

  • L’adoption des applications réactives est lente.

  • Reactor est un moteur qui permet de programmer des applications réactives. 

  • WebFlux est l’équivalent de Spring MVC dans le monde réactif.

  • WebFlux support la backpressure.

  • Pour faire un web service réactif il faut une base de données qui supporte les API réactives.

  • Spring Data peut être utilisé pour les repository réactifs.

  • R2DBC peut être utilisé pour les bases de données SQL réactives supportées.