Accueil Nos publications Blog Architecture et programmation réactives avec Akka et Scala – Partie 1

Architecture et programmation réactives avec Akka et Scala – Partie 1

Ce blog post est la première partie d’une série de 3 articles qui introduisent la programmation réactive dans la pratique avec la bibliothèque Akka et le langage de programmation Scala.
Dans cette première partie, on va rappeler le contexte global qui a mené à l’apparition de ce nouveau paradigme de programmation, puis faire un rapide tour d’horizon des bibliothèques ciblant la JVM qui l’implémentent, pour finir enfin avec l’introduction de son architecture commune de base.

Petit rappel, contexte et initiatives

Encore une fois, un nouveau buzzword arrive dans le monde du développement, faisant vibrer une bonne partie de la communauté : comme le titre le suggère, il s’agît de la programmation réactive.
On ne va pas trop s’attarder et présenter (encore une fois) ce nouvel arrivant puisque c’est déjà fait par SOAT : ce livre blanc (téléchargeable ici) démystifie ce nouveau terme et introduit le concept général.
Voici tout de même un petit rappel du contexte global pour gâter les plus paresseux d’entre nous 😃

Contexte

  • Le modèle client serveur classique
    Dans la représentation ci-dessous, on affiche comment un unique utilisateur interroge un serveur web pour retrouver une information. Le serveur peut soit construire la réponse par lui-même, soit faire appel à d’autres sous-services (ici les serveurs Service A, Service B, Base de données)

  • Plus de clients… BEAUCOUP plus de clients
    Jusqu’à présent, servir un seul ou un nombre limité de clients ne posait pas de problème. Le serveur a le temps de traiter la requête : recevoir la demande, la traiter, faire les appels internes, puis construire la réponse et la retourner.
    Mais si le nombre de demandes des clients venait à augmenter de façon considérable ?

    Les premières approches apportées sont :
    Vertical Scaling : on augmente simplement les capacités physiques de la machine serveur (Processeur, RAM)
    Horizontal Scaling : on ajoute plus de machines serveurs pour gérer la grande charge, ce qui introduira un degré de complexité important.
    D’un côté, la charge sur le serveur Web principal sera transmise aux serveurs en aval, il faudra donc augmenter leur nombre (retour à la problématique de départ). D’un autre côté, si on dispose d’une ressource IO (lecture/écriture sur base de données par exemple), on introduira une problématique d’accès concurrentiel. En d’autres termes, il faudra répondre aux questions : Quand mettre à jour la donnée ? Qui le fera ?
    La représentation ci-bas met en évidence ce scénario :

À ce moment-là, il devient clair qu’il y a de la place pour introduire une approche différente de celles qui existent déjà (à savoir la programmation réactive).
S’appuyant sur une génération de processeurs sophistiqués multi-thread et multi-cœur, ce paradigme de programmation propose un modèle qui mêle mutli-threading, asynchronisme, rythme d’exécution, afin de fournir les outils de construction d’applications non bloquantes.
Le résultat final offrira une expérience utilisateur considérablement plus fluide, qu’on qualifiera aussi de plus réactive.

Initiatives & implémentations

D’importants acteurs de la communauté tels que les “tech companies” Netflix, Pivotal et Lightbend ont commencé tôt à mettre en place des frameworks ou des bibliothèques qui implémentent le paradigme de la réactivité. Cela est dû au fait qu’il fallait faire face à un nombre grandissant d’utilisateurs à servir (le cas de Netflix) qui étaient très exigeants en termes de temps réponse. Autrement formulé : un service très réactif était requis.

Dans un deuxième temps, les frameworks et bibliothèques implémentés ont fait apparaître des similitudes conceptuelles très proches. Cela a poussé ces acteurs, dans un effort d’unification des principes des systèmes réactifs, à rédiger un document de spécification unique définissant et décrivant ce qu’est un système réactif. Ce document a été nommé “The reactive manifesto” et a été rendu public (consultable ici).

On notera aussi que suite à tout cela, Java dans sa version 9 a introduit les interfaces déclarant le comportement et actions pour de potentielles futures implémentations de bibliothèques réactives.
Parmi les implémentations déjà mises en place avant la spécification Java 9, on pourra citer :

  • Akka (Lightbend)
    Initialement Akka est un ensemble de modules qui propose un modèle de concurrence asynchrone, basé sur un système d’acteur (ActorSystem) échangeant des messages, comme alternative au modèle Java de multi-threading et de synchronisation bloquante.
    Avec son module “core” Akka Actors, Akka propose aussi un sous-module, Akka Streams, comme son implémentation de la réactivité. S’appuyant sur les acteurs du système d’acteur principal, Akka Streams modélise un flux d’entités/objets passant avec un rythme donné dans ce qui ressemble à un réseau de plomberie interconnecté. Au fur et à mesure de son avancement, l’entité/objet subit des transformations.
    L’ensemble Akka s’adresse à la fois aux langages Java et Scala. Une implémentation Akka.NET existe aussi pour l’environnement .Net.

  • ReactiveX (Netflix – pour JVM)
    D’un concept originel venant de Microsoft, repris et développé pour la JVM par Netflix, cette bibliothèque rassemble plusieurs langages. Elle est abréviée plus communément avec Rx* où l’astérisque * indique le langage ciblé. En d’autres termes, on aura explicitement (liste non exhaustive) :
  • RxJava pour Java
  • RxScala pour Scala
  • Rx.Net pour C#
  • RxJS pour JavaScript
  • RxPy pour Python
  • ReactiveX se propose comme une implémentation de la réactivité qui en quelque sorte n’impose pas un mode opératoire à son utilisateur. Le développeur pourrait importer la bibliothèque et aura la main sur son mode d’exécution : Plutôt ThreadPool ou ActorSystem ? Si c’est un ThreadPool de quel type sera-t-il ? C’est à lui de faire le choix adéquat.
    On remarquera une certaine différence avec Akka Streams qui par défaut et dans la majorité des cas utilisera le système d’acteur comme moteur d’exécution.
    Rx* utilise les notions de Observable (observé) et Subscriber (souscripteur/abonné) pour modéliser un flux d’évènements/objets émis par une entité observée puis reçus par une autre abonnée.
    Il existe aussi au sein de la bibliothèque plusieurs autres opérations permettant de fusionner, filtrer, ou même agréger le flux.

  • Reactor (Pivotal)
    Reactor ou Project Reactor est une autre implémentation de la programmation réactive qui cible essentiellement les technologies Java. Malgré un mode de fonctionnement très similaire à celui de ReactiveX, cette bibliothèque a su se démarquer grâce à son intégration au très célèbre Spring Framework.
    Soutenu par Pivotal (la compagnie derrière Spring framework) la bibliothèque a été développée et intégrée à partir de la version 5 de Spring Framework. Désormais deux “stack” coexistent dans le framework : la Servlet Stack classique/bloquante et la Reactive Stack. Cette dernière propose toute une pile réactive qui va de la couche Web (Serveur Netty, Servlet,Spring WebFlux) jusqu’à la couche Repository et Data (MongoDB,Couchbase).

Architecture commune

Toutes ces bibliothèques implémentant la programmation réactive partagent un même mécanisme de fonctionnement. Quelques termes ont fait allusion à cette propriété dans les paragraphes précédents, termes comme : Flux, Émission, Évènement.

Le schéma qui va suivre représente l’unité de base régissant le fonctionnement d’un système réactif :

Un objet sera émis depuis une source Publisher et passera à travers un Operator pour subir une transformation ou une opération. Il est reçu par le Subscriber dans l’étape finale où il sera sujet de la dernière opération.
À noter qu’il est possible de concaténer plusieurs Operators en cascade et donc d’enchaîner les opérations de transformation sur l’objet émis.
Si on considère par la suite plusieurs objets émis depuis le Pusblisher, passant à travers le ou les Operators et arrivant au Subscriber, on remarquera alors qu’un flux (terme à retenir) commence à prendre forme. Si de plus on rajoute une cadence ou un rythme (N éléments par seconde) aux passages des objets à transformer, le flux deviendra plus ordonné.
On pourra faire l’analogie avec une chaîne de montage industrielle sur laquelle travaillent des opérateurs avec un certain rythme de production.
Note : Si un Operator (comme illustré) ou un Subscriber n’arrive pas à terminer la transformation avant l’arrivée d’un nouvel objet émis, il y aura envoi d’un signal en amont pour indiquer qu’il faudra réduire le rythme d’émission. On parlera alors de Backpressure.

Récapitulatif partie 1

Voilà ! 😃
On a atteint ici la fin de cette première partie introductive à l’architecture réactive. Dans ce qui a précédé, on a revisité le besoin menant à adopter une architecture réactive dans une application Web, on a fait un rapide tour des bibliothèques qui implémentent la réactivité, puis on a terminé avec la présentation de l’architecture commune de base.

Dans les parties qui suivront on s’intéressera à des aspects plus avancés de Akka Streams qui lui confèrent toute sa puissance en s’appuyant sur du code et des exemples pratiques.

© SOAT

Toute reproduction interdite sans autorisation de la société SOAT