Article écrit par Linda Ben Hadj Naceur
De l’impératif vers le réactif
De nos jours, les systèmes informatiques sont de plus en plus complexes, et les demandes utilisateurs beaucoup plus difficiles à suivre. Pour cela, et pour certaines applications, le passage vers une architecture réactive s’est manifesté comme un besoin impérieux.
Avec les paradigmes “classiques” comme la programmation impérative, le code prend très rapidement de l’ampleur et perd en clarté. Les optimisations et les extensions sont plus difficiles à gérer et présentent plus de risques d’erreurs.
Pour remédier aux limites du monde impératif, le Manifeste Réactif parle de systèmes qui doivent toujours être disponibles, être robustes, souples et orientés message : “Nous les appelons Systèmes Réactifs”.
Les facteurs d’un système réactif
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. Cela peut être fourni grâce à des E/S non bloquantes (Modèle Single Thread Non Bloquant).
Résilients : le système doit rester disponible en cas de pannes: par réplication de fonctionnalité (différents threads ou nœuds disponibles), l’isolement de composants (communication asynchrone) et la délégation de responsabilité (délégation des tâches entre composants).
Souples : le système doit rester réactif face à des charges de travail imprévisibles. Dans ce cas on parle d’un système auto-scalable, ce qui signifie que les instances de l’application augmentent ou diminue en fonction de la charge de travail.
Orientés messages : Le système utilise la communication par message asynchrone entre les différents composants pour assurer un couplage faible.
Le Modèle Single Thread Non Bloquant
Pour parler de programmation réactive ou de programmation non bloquante, il faut commencer par présenter le principe de fonctionnement du Single Thread Non Bloquant.
Ce modèle permet de mieux gérer les ressources du serveur et d’optimiser au maximum ses différentes ressources. Contrairement au modèle classique qui est le Multi Threads Bloquant, le Single Thread Non Bloquant permet d’éviter la monopolisation des ressources et les problèmes de latence.
1/ Le client envoie une requête au serveur.
2/ Un thread unique orchestre les entrées sorties non bloquantes.
3/ Le IO thread gère les lectures/écritures comme des événements à empiler/dépiler dans une queue d’une manière non bloquante.
4/ Il affecte la demande du client à l’un des threads internes. Les workers threads traitent les requêtes et préparent la réponse finale.
5/ La boucle d’événement renvoie cette réponse au client respectif.
Ainsi, les ressources du serveur seront toujours exploitées au maximum.
Reactive Streams
Reactive Streams est une norme pour le traitement de flux asynchrone avec communication non bloquante.
Le schéma ci-dessus représente les principaux facteurs de cette norme :
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.
Reactor API
Reactor API est une implémentation, développée par Spring, de la spécification Reactive streams.
La bibliothèque Reactor 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.
La Stack Reactive Spring
Spring WebFlux
Avec la version 5 de Spring et en parallèle avec la stack Spring WebMVC, Pivotal ajoute un support dédié complètement à la programmation réactive : le Spring WebFlux.
Le Framework Spring WebFlux est entièrement non bloquant, prend en charge la contre-pression (Back pressure) des flux réactifs et s’exécute sur des serveurs tels que les conteneurs Netty, Undertow et Servlet 3.1+.
Spring WebFlux continue à utiliser les mêmes annotations du contrôleur Spring MVC (@Controller, @RequestMapping…) avec les types de retour Flux<T> et Mono<T>.
Reactive WebClient
Spring WebFlux 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.
Les limites de la programmation Réactives
Comme tout autre paradigme de programmation, le paradigme réactive a 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.
Un exemple d’application
Pour mieux comprendre la programmation réactive, implémentons un petit exemple applicative qui repose entiérement sur l’exemple officel de Spring “Building a Reactive RESTful Web Service”.
On va suivre le schéma d’architecture ci-dessus :
- Commençons par un simple POJO : Article.java avec les setters et les getters nécessaires.
- Pour faciliter l’exemple, on ne va pas créer des repositories pour la couche Data. Notons juste qu’il faut utiliser Spring Data Réactive Repositories pour rester dans un contexte non bloquant.
- Un Handler : en équivalence à un Service pour la stack WebMvc, le Handler permet de gérer les requêtes et les réponses envoyés aux clients.
Cette classe permet de retourner un Article en format Json.
- Un Router : en équivalence à un Controller pour la stack WebMvc, il permet de gérer la route pour exposé les services.
Le Router reste en écoute pour renvoyer ce que le handler lui fournit. Dans notre exemple un Article (Le chemin /article).
- Et finalement 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.
En exécutant l’application, on obtient le résultat suivant : message = Article 1
Conclusion
Pour conclure, On peut affirmer que l’utilité de la programmation réactive n’est plus un sujet à débattre. Pivotal, Netflix et d’autres ont tous contribué à présenter une stack réactive complète et cohérente. Reste à définir si votre projet nécessite un environnement réactif ? En d’autres termes, pour basculer vers un environnement réactif, il faut être sûr que c’est la meilleure solution, sinon on va se compliquer la vie pour rien.
En revanche, pour tirer pleinement profit du système réactif, il faut s’assurer d’avoir une application Réactive de bout en bout.