logo le blog invivoo blanc

Programmation Réactive

2 novembre 2022 | Java | 0 comments

De nos jours, les applications modernes ont de hautes exigences en matière de disponibilité et de demandes utilisateurs. Sauf qu’avec les paradigmes “classiques” comme la programmation impérative, le code prend très rapidement de l’ampleur. De ce fait, les optimisations et les extensions deviennent plus difficiles à gérer et favorisent plus le risque d’erreurs. 

Afin de répondre à ces nouvelles exigences, le paradigme “Manifeste Réactif” est apparu afin de donner aux systèmes une plus grande tolérance vis-à-vis des erreurs. Ayant des temps de réponse rapides, les systèmes réactifs permettent aux utilisateurs une interaction accrue et une plus grande satisfaction

Le Manifeste réactif

Il s’agit d’un paradigme de programmation qui repose sur l’émission de données à partir d’une source unique ou de plusieurs sources. Ce paradigme s’est inspiré du patron de conception Observateur et vise à 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. 

Abonné (Subscriber) : c’est le demandeur et le consommateur des données. 

Editeur (Publisher) : C’est l’émetteur des données demandées par l’abonné. 

Les échanges entre l’éditeur (Publisher) et l’abonné (Subscriber) sont gérés par la notion de gestion de demande (BackPressure). L’abonné (Subscriber) demande les données selon sa capacité de traitement. Et l’éditeur (Publisher) ne renvoie que les données demandées pour éviter de le submerger. 

Caractéristiques

Selon le Manifeste, les systèmes réactifs doivent être : 

Disponibles : 

 Le système doit fournir un temps de réponse rapide et cohérent.  

Résilientes : 

Le système doit toujours rester disponible, même en cas d’erreur, et ce grâce aux différents threads ou nœuds disponibles, à la communication asynchrone et la délégation des tâches entre composants. 

Souples 

Le système doit continuer à vivre même s’il est surchargé. On parle ici d’un système auto-scalable, càd que les instances de l’application augmentent ou diminuent en fonction de la charge de travail. 

Orientées messages 

Le système utilise des messages asynchrones entre les différents composants. 

La Stack Reactive Spring

Parmi les implémentations de ce modèle de programmation réactive, on retrouve la bibliothèque Reactor qui se compose de deux types de flux de données (Streams) : 

Flux : un éditeur (Publisher) qui correspond à un échange de 1 à n éléments 

Mono: un éditeur (Publisher) qui correspond à un échange de 0 à 1 élément. 

 
Ainsi que le framework Spring WebFlux qui inclut une API WebClient pour assurer la communication d’une manière réactive des applications java distribuées. 

Contrairement à l’API REST, le WebClient est entièrement non bloquant, et prend en charge le streaming. 

Exemple applicatif

Pour mieux comprendre la programmation réactive, implémentons un petit exemple qui repose entièrement sur l’exemple officiel de Spring “Building a Reactive RESTful Web Service”. 

Commençons par :

Un simple POJO :

public class Book { 
  private String title; 
  public Book() { 
  } 
  public Book(String title) { 
    this. title = title; 
  } 
  public String getTitle() { 
    return this. title; 
  } 
  public void setTitle(String title) { 
    this. title = title; 
  } 

  @Override 
  public String toString() { 
    return "Book{" + 
        "title='" + title+ '\'' + 
        '}'; 
  }} 

Pour faciliter l’exemple, on ne va pas créer des repositories pour la couche Data. Notons juste qu’il faut utiliser Spring Data Reactive Repositories pour rester dans un contexte non bloquant. 

Un handler

En équivalence à un Service pour la stack Spring WebMVC, le Handler permet de gérer les requêtes et les réponses envoyées aux clients. Cette classe permet de retourner le POJO en format Json. 

@Component 
public class BookHandler { 

  public Mono<ServerResponse> hello(ServerRequest request) { 
    return ServerResponse.ok().contentType(MediaType.APPLICATION_JSON) 
      .body(BodyInserters.fromValue(new Book("Homo Deus"))); 
  } 
} 

Un Router

En équivalence à un Controller pour la stack WebMVC, il permet de gérer la route pour exposer les services. 

Le Router reste en écoute pour renvoyer ce que le handler lui fournit. Dans notre exemple Book (le chemin /book).

@Configuration(proxyBeanMethods = false) 
public class BookRouter { 
  @Bean 
  public RouterFunction<ServerResponse> route(BookHandler BookHandler) { 
    return RouterFunctions 
      .route(GET("/book") 
     .and(accept(MediaType.APPLICATION_JSON)), BookHandler::getTitle); 
  } 
} 

Un WebClient

Comme point d’entrée pour une communication non bloquante avec l’application. 

La classe qui représente le WebClient, agit d’une manière réactive pour renvoyer un Mono qui contient le libellé de l’article. 

@Component 
public class BookClient { 
  private final WebClient client; 
  // Spring Boot auto-configures a `WebClient.Builder` instance with nice defaults and customizations. 
  // We can use it to create a dedicated `WebClient` for our component. 
  public BookClient(WebClient.Builder builder) { 
    this.client = builder.baseUrl("http://localhost:8080").build(); 
  } 
 
  public Mono<String> getMessage() { 
    return this.client.get().uri("/book") 
        .accept(MediaType.APPLICATION_JSON) 
        .retrieve() 
        .bodyToMono(Book.class) 
        .map(Book::getTitle); 
  }} 

En exécutant l’application, on aura le message >> Homo Deus 

Conclusion

A l’heure actuelle, on peut citer beaucoup de plateformes qui présentent une stack réactive complète et cohérente tels que Netflix, pivotal… Faut-il migrer à un environnement réactif ? 

Il est bon à savoir qu’avant de basculer vers un système réactif, il faut être sûr que le besoin se présente réellement et que c’est la solution la plus adaptée, sinon on va se compliquer la vie pour rien car le paradigme réactif présente ses propres limites : 
 

  • Une implémentation complexe : il faut changer sa manière de penser (il faut penser en flux de données), utiliser des types de retours différents (Mono, Flux…). 
  • Un Suivi difficile : la programmation réactive repose entièrement sur des communications asynchrones assez difficile à suivre et à tracer. 
  • Une migration conséquente : la migration vers une application réactive, doit être faite de bout en bout, en commençant par le client et en arrivant jusqu’à la base de données. 

Sources

https://spring.io/guides/gs/reactive-rest-service/

https://blog.ouidou.fr/la-programmation-r%C3%A9active-12f342cef911

https://ryax.tech/fr/la-programmation-reactive-cest-quoi/

https://www.reactivemanifesto.org/fr