Event driven architecture : enjeux et solutions

Comment favoriser la communication et la collaboration de l’ensemble des sous-systèmes d’un SI complexe ? En s’échangeant des messages décrivant des événements par exemple.

Cet article est le premier d’une série sur une vision de l’architecture événementielle et d’une implémentation possible (avec Apache Kafka).

Je travaille actuellement pour un site dont le but est de mettre des personnes en relation, de les faire communiquer, interagir. (Bon, ok, et aussi se voir en vrai et pourquoi pas se faire des bisous, des bébés, l’amour toussa toussa).

Or, très souvent, dans un SI complexe, les sous-systèmes impactés par une action sur le site ou l’application mobile sont nombreux. On peut citer sans être exhaustifs:

  • Le système de notification in-app,
  • Le système d’envoi d’ e-mails de notification à l’utilisateur recevant (voire à l’utilisateur envoyant),
  • Le système d’envoi de push mobile (iOS, Android),
  • La détection de fraude,
  • L’indexation dans le moteur de recherche

Bref, ça fait du monde dans la boucle, et très souvent des équipes avec chacune leur métier, leurs contraintes, leur organisation.

Là, vous avez plusieurs possibilités (je les évoque même si elles sont moches).

Soit vous avez un SI SQL-centric, et vous décidez de mettre en place des triggers déclenchant les mises à jour

Assez rapidement, vous avez besoin de mettre en place des tables de log qui sont parcourues par des batchs mettant à jour ou interagissant avec les sous-systèmes concernés.

Tant que le nombre de sous-systèmes concernés est faible (1 ou 2), ça peut marcher (d’ailleurs, dans certaines sociétés, ça marche comme ça pendant des années).

Malheureusement, cela veut dire que vous vous reposez sur votre système de stockage pour gérer de la logique business. D’expérience, c’est rarement une bonne idée car cela donne un côté magique à votre SI, et vous vous retrouvez avec un système difficile à faire évoluer (pas de versionning du code, process de déploiement à haut risque,…)

De plus, quand le nombre de sous-systèmes augmente, ou même que le nombre d’événements déclenchant une mise à jour augmente, vous augmentez d’autant le nombre de triggers à poser, ce qui ralentit d’autant les performances de votre stockage.

Soit vous déclenchez les interactions avec les sous-systèmes directement dans le traitement de votre événement métier au niveau site ou API

Ici, plus de magie, et votre équipe de développement a la pleine maîtrise de ce qui se passe lors d’une action de vos utilisateurs.

Ici encore, tant que le nombre de sous-système est faible, cela peut être admissible. Mais vous vous retrouvez avec un couplage fort entre votre site/api et les autres sous-systèmes. Un nouveau sous-système à notifier ? Et vlan, modification de votre code pour ajouter les appels de mise à jour.

Qui plus est, si comme moi votre environnement est en PHP, l’asynchronisme n’est pas natif et suppose de l’huile de coude et toute une mécanique.

Bref, ce n’est pas une bonne idée non plus dans un SI complexe.

Soit vous mettez en place un système de traitement d’événements asynchrones, via un bus d’événement.

Et c’est évidemment ce que je me propose de vous présenter ici.

L’idée est de mettre à disposition de votre SI un composant de communication entre sous-systèmes.

Architecture événementielle vue d'avion

« Tu vois, le monde se divise en 2 catégories… ceux qui ont un pistolet chargé, et ceux qui creusent. »

Dès lors, vous devez considérer 2 types de traitement : les producteurs et les consommateurs. Les producteurs génèrent des événements à destination de votre bus, alors que les consommateurs se mettent à l’écoute de l’arrivée de nouveaux événements (par type, elles ne sont pas obligées de tout traiter… chacune son taf !).

Bien sûr, un consommateur peut très bien, lors de son traitement, avoir l’envie soudaine d’émettre un événement et jouer alors le rôle de producteur. Et c’est une bonne pratique car cela permet un découplage encore plus important de vos traitements (par exemple, pour séparer les différentes étapes d’un workflow). Evidemment, la difficulté est de bien cartographier les flux d’événements pour éviter les usines à gaz.

Quelles solutions pour le bus d’événement ?

Il existe bien des solutions, de la plus rudimentaire à la plus complexe. Voici quelques unes de celles que j’ai regardées :

Beanstalkd

C’est sans doute le système le plus rudimentaire de la liste. Il a un avantage énorme : son protocole super simple, un peu à la memcached, le rend très simple à intégrer avec n’importe quel langage, même PHP ! Un simple socket suffit à communiquer avec lui. De ce fait, on a des performances vraiment intéressantes en production et consommation.

Par contre, rançon de sa simplicité, pas de redondance nativement disponible, ni de failover. Bref, ce n’est pas là-dessus que l’on peut s’appuyer comme colonne vertébrale de la communication inter-système.

Néanmoins, pour du spool local, c’est une très bonne solution (on peut ainsi gérer des tâches asynchrones, même avec un langage qui ne le permet pas).

ActiveMQ

Dans la famille Apache, je demande le grand frère ! ActiveMQ est un peu la référence classique en terme de messaging dans le monde Java. Implémentant la spécification JMS, pouvant être utilisé aussi bien en publish and subscribe (le mode qui nous intéresse ici) qu’en one-to-one (envoi de message à une application destinatrice spécifique), il a l’avantage de la polyvalence.

Cependant, il n’est pas conçu pour absorber efficacement de forte charge de messages, et il est finalement assez vite consommateur de ressources système.

C’est néanmoins un système qui se monte vite et très interopérable (avec un monde PHP, il est possible d’utiliser un protocole assez léger comme STOMP).

RabbitMQ

Ecrit en Erlang, il est sur le papier capable d’absorber de fortes charges. Beaucoup de sociétés l’ont implémenté avec succès (citons Blablacar, LaFourchette, Viadeo,…) et avec beaucoup de satisfaction.

Il est également très polyvalent avec son système d’exchange, qui permet de router des messages en fonction de leur contenu, de faire du publish and subscribe, du one-to-one, du traitement RPC (avec les queues de réponses), voire du différé (en truandant un peu le système des dead-letters).

AMQP est un protocole très bien supporté par beaucoup de langages.

Bref, une très bonne solution… même si ce n’est pas celle que j’ai retenue (pour l’instant). En effet, je trouve le système de gestion des exchanges trop complexe pour ce que je souhaite mettre en place. Peut-être ai-je tort, mais rien n’est définitif de toute façon.

Et comme l’a dit un jour un grand homme :

« Il n’y a que les imbéciles qui ne changent pas d’avis, c’est mon avis depuis toujours ! »

Kafka

Dans la famille Apache, je demande le petit dernier. Issu de chez Linkedin, ce système orienté « logs distribuées » est très polyvalent, très souple. Il permet de conserver l’historique des messages (pour les retardataires qui ne s’enregistreraient que tardivement) et peut sur le papier absorber des tonnes d’informations.

Ses défauts : une stabilité qui reste à démontrer, un protocole un peu changeant (la dernière version à l’heure de la rédaction de cet article est la 0.8.1.1, en rupture de protocole par rapport à la 0.7), et une difficulté de mise en place en répartition sur 2 datacenters en assurant le DRP (principalement à cause de l’utilisation sous-jacente de Zookeeper et ses impératifs de quorum).

Mais les promesses en terme de tenue en charge et de fonctionnalités sont tellement bonnes que ça vaut la peine de s’y essayer.

Vous l’aurez compris, c’est sur ce système que je suis parti, et ce sera le sujet de la deuxième partie de cette série d’articles.

2 réflexions au sujet de « Event driven architecture : enjeux et solutions »

  1. Salut grand chef !
    Merci pour cet article, je ne connaissais pas Kafka, je regarderai ce que ça donne.

    Effet de mode oblige, j’utilise RabbitMQ en ce moment. Le besoin est archi simpliste : un backoffice envoie un message lors d’une modification, ajout ou suppression d’un document (NoSQL) en base.
    Le message contient le type d’évènement (update, create ou delete), le type du document en question et son uuid. Les consumers à l’écoute attaquent ensuite l’API REST du BO pour récupérer les infos et mettent à jour leur BDD.
    Du coup, je n’utilise pas (pour l’instant) le routage de RMQ, pas assez de sous-systèmes pour ça. Et vu la facilité de mise en place (surtout avec une recette Puppet qui va bien), c’est une solution qui nous apporte une grande satisfaction 😉

    1. RabbitMQ est un système qui marche très bien, je n’ai aucun doute là-dessus. Il est utilisé chez plein de grands du web. Mais pour le volume que j’imagine au final, je ne pense pas qu’il tiendra. De plus, comme je l’écrirai dans mon prochain article, le vrai intérêt de l’outil est de conserver les événements sans se prendre la tête à créer des queues. C’est vraiment lié à l’approche que je souhaite mettre en place, mais ce n’est évidemment pas une vérité universelle 🙂

Laisser un commentaire

Votre adresse de messagerie ne sera pas publiée. Les champs obligatoires sont indiqués avec *