logo le blog invivoo blanc

La programmation réactive dans une JVM près de chez vous !

26 août 2016 | Front-End, Java

La programmation réactive dans une JVM près de chez vous !

Attention ! Nouvelle mode à l’horizon. Les développeurs front la suivent déjà depuis des lustres mais les développeurs Java d’entreprise tels que moi sont généralement à la traîne sur ce qui est trendy.

Ce temps de latence a ses avantages et ses inconvénients. Personnellement, je m’efforce de tout savoir et tout connaître pour avoir une opinion sur tout. De cette manière, je peux ajouter chaque nouvelle techno / librairie à ma boîte à outils. Dès qu’on me formule un besoin, je plonge dans cette boîte et j’en sors celui qui est le plus adapté.

Il m’aura fallu près d’un an pour déterminer si je devais mettre la programmation réactive dans ma boîte à outils ou pas. Apprivoiser la bête n’est pas simple tellement elle change nos fondamentaux de la programmation Java. Le plus dur au final aura été de trouver une simple explication de pourquoi la programmation réactive sauvera tous nos vies.

Le pattern de l’Observable sur les stéroïdes

Je fais un énorme raccourci et vulgarise à outrance pour expliquer ce qu’est la programmation réactive. Je ne serai pas académique dans l’approche mais plutôt pragmatique. De toute façon l’approche académique vous endormirais avant la fin du billet.

Concrètement, la programmation réactive est un modèle de programmation où les appels entre dépendances sont totalement asynchrones. Par conséquent, tout traitement est le résultat d’un événement. Bref, tout résultat d’appel est géré par des callbacks. En conséquent, tout notre code devient non bloquant.

En adoptant un code non bloquant, on réduit considérablement le nombre de threads qui sont mis en sleep le temps que l’appel d’une dépendance soit terminé. On devient donc économe sur le nombre de threads à utiliser dans notre application.

Un exemple classique d’appel bloquant.

/*
* This code will block the thread until the database respond
* with the result.
*/
List users = userService.findAllUsers();

LOGGER.info(users.size() + " users were found in the database.");

Considérons que ce code est appelé 10 fois en parallèle, on se retrouve avec 10 threads qui sont portés en SLEEP le temps que la base de données réponde. Le processeur va donc faire du context switching sur tous les threads jusqu’à ce que les traitements sont complétés. Le context switching d’un CPU est du temps perdu pour rien. La programmation réactive vise donc fondamentalement à nous éviter de bloquer sur ces traitements afin que le CPU puisse être utilisé pour autre chose (sauver le monde par exemple…).

Un exemple de code non bloquant avec Reactor 2.5

/*
* Ce code ne bloquera pas le thread. `subscribe` prend en paramètre une lambda représentant une
* callback qui sera appelé lors que le service aura terminer de récupérer la liste des users.
*/
userService.findAllUsers().doOnNext(users -> {
// Faire quelque chose avec les users.
LOGGER.info(users.size() + " users were found in the database.");
}).subscribe();

Dans cet exemple, findAllUsers retourne un Publisher sur lequel nous pouvons définir le traitement à opérer une fois que la base de donnée aura produit le résultat. Chaque méthode sur un Publisher retourne une référence de lui-même. Ce type d’api permet donc de chainer les résultats que nous voulons appliquer un fois que le résultat sera produit. L’avantage réside dans le fait qu’un service peut appliquer les traitements qu’il désire une fois l’évènement de résultat compléter et donner la chance à ses appelants de chainer leurs propre traitements une fois que le dit service aura traité le résultat de la base de donnée.

Nativement, Java n’est pas outillé pour ce style de programmation. Avec 1.5, nous avons hérité de l’api java.util.concurrent qui nous a donné les Future.

Future usersFuture = databaseService.findAllUsers();

// Bloque jusqu'à ce que la base de données réponde.
List users = usersFuture.get();

Il s’agit certes d’un premier pas, mais pour adopter un modèle 100% asynchrone, ce n’est pas adapté. L’utilisation des futures nous amènes vers un « Callback Hell ». De plus, aucune opération n’est possible sur les Future. Les librairies en vogue remplacent le future par un Observable pour RxJava et un Publisher pour Reactor. Ces objets permettent d’enchaîner les traitements qui devront être réalisés sur le résultat du service. Ainsi on peut transformer, merger, grouper et filtrer les événements qui résultent du service. En finalité, le développeur se retrouvera à coder deux aspects. Les traitements d’événements et les liens sur la chaîne de ces événements. Le traitement métier de notre application se retrouve donc découplé de la manière dont il sera appelé mais aussi géré (multi-threadé, simple thread, regroupé en paquets, bloquant, asynchrone, etc) tout en assurant un traitement optimal par le CPU plutôt que devoir opérer à grand coup de thread pool dans l’espoir d’améliorer les perfs de l’application (MOOOAAAAR THREADS !).

La programmation par événements, ce n’est pourtant pas nouveau

Non ce n’est pas nouveau ! Non ce n’est pas une révolution ! La programmation réactive au gout du jour c’est surtout une évolution. Théoriquement, rien n’a été inventé. Par contre, aux yeux de codeurs, c’est une véritable révolution (de nos jours on assiste à une « révolution » tous les 3 mois mais c’est pas grave..). J’ai vu de mes yeux des applications codé en 100% réactif mais basée sur un callback hell. Les dernières générations de librairies réactives nous donnent une énorme boîte à outil qui permettent de manipuler et transporter les résultats des services asynchrones.

reactive-all-the-things

La mode réactive est relativement jeune. Tout le legacy de l’échosystème Java est quand à lui généralement bloquant. Exemple, JDBC et les différentes libs HTTP sont bloquantes. Par conséquent, tout le travail fait de manière asynchrone se retrouve « gâché » par l’appel de ces dépendances qui seront inévitablement bloquantes. On doit dès lors implémenter des « wrappers » qui vont donner une apparence réactive mais qui au final ne le seront pas. La solution viendra au fil du temps par le développement d’APIs qui seront nativement réactives jusqu’au drivers JDBC. Les drivers Cassandra et Elasticsearch sont par exemple déjà non bloquants.

On ne s’arrête surtout pas là et on va plus loin.

Cet article précède un compte rendu exhaustif de la conférence DEVOXX 2016 « Architecture découplée grâce aux Reactive Extensions » qui vous sera proposé par un autre collaborateur INVIVOO et sera l’occasion d’un mois de la programmation réactive en septembre prochain sur ce blog.

Commençons par un petit manifeste qui est à l’origine de la mode de la programmation réactive.

The Reactive Manifesto

Ensuite, je vous relègue l’article qui a réussi à me donner l’illumination divine du pourquoi je devais coder en réactif.

The introduction to Reactive Programming you’ve been missing

Et finalement ! La librairie Java qui à mes yeux prendra le dessus sur RxJava de Netflix :

Project Reactor

Et finalement, sans celle-ci, Reactor ne serait pas à l’ordre du jour :

RxJava

much-reactive