Architecture basée sur des événements avec Pub/Sub

Ce document traite des différences entre les architectures sur site basées sur la file d'attente de messages et les architectures cloud basées sur des événements mises en œuvre sur Pub/Sub. En appliquant les modèles sur site directement aux technologies cloud, il est possible que la valeur unique qui rendait le cloud attrayant en principe soit ignorée.

Ce document est destiné aux architectes système qui migrent des conceptions d'architectures sur site vers des conceptions dans le cloud. Dans ce document, nous partons du principe que vous maîtrisez les systèmes de messagerie.

Le schéma suivant présente un modèle de file d'attente de messages et un modèle Pub/Sub.

Compare l'architecture d'un modèle de file d'attente de messages avec un modèle basé sur des événements à l'aide de Pub/Sub

Dans le schéma précédent, un modèle de file d'attente de messages est comparé à un modèle de flux d'événements Pub/Sub. Dans un modèle de file d'attente de messages, l'éditeur envoie des messages dans une file d'attente où chaque abonné peut écouter une file d'attente particulière. Dans le modèle de flux d'événement à l'aide de Pub/Sub, l'éditeur envoie les messages à un sujet que plusieurs abonnés peuvent écouter. Les différences entre ces modèles sont décrites dans les sections suivantes.

Comparer les flux d'événements et la messagerie basée sur la file d'attente

Si vous travaillez avec des systèmes sur site, vous maîtrisez déjà les bus de service d'entreprise (ESB) et les files d'attente de messages. Les flux d'événements constituent un nouveau modèle et présentent des différences importantes ainsi que des avantages concrets pour les systèmes modernes en temps réel.

Ce document décrit les principales différences entre le mécanisme de transport et les données de charge utile dans une architecture basée sur des événements.

Transport des messages

Les systèmes qui déplacent des données dans ces modèles sont appelés agents de service de messagerie, et différents frameworks sont mis en œuvre. L'un des premiers concepts est le mécanisme mécanique qui achemine les messages de l'éditeur vers le destinataire. Dans les frameworks de messages sur site, le système d'origine émet un message explicite, découplé, vers un système de traitement en aval en utilisant une file d'attente de messages comme transport.

Le schéma suivant illustre un modèle de file d'attente de messages :

Les messages d'un éditeur sont envoyés vers une file d'attente unique pour chaque abonné

Dans le schéma précédent, les messages circulent depuis un processus d'éditeur en amont vers un processus d'abonné en aval à l'aide d'une file d'attente de messages.

Le système A (éditeur) envoie un message à une file d'attente sur l'agent de messagerie désigné pour le système B (abonné). Bien que l'abonné de la file d'attente puisse être composé de plusieurs clients, tous ces clients sont des instances en double du système B déployées pour le scaling et la disponibilité. Si des processus en aval supplémentaires (par exemple, le système C) doivent consommer les mêmes messages du producteur (système A), une nouvelle file d'attente est requise. Vous devez mettre à jour le producteur pour publier les messages dans la nouvelle file d'attente. Ce modèle est souvent appelé transmission de messages.

La couche de transport de messages de ces files d'attente peut ou non fournir des garanties d'ordre des messages. Souvent, les files d'attente de messages fournissent un modèle garanti par ordre, dont les données sont séquencées selon un modèle strict de type "premier entré, premier sorti" (FIFO), semblable à une file d'attente de tâches. Au départ, ce modèle est facile à mettre en œuvre, mais il pose parfois des défis en matière de scaling et d'exploitation. Pour mettre en œuvre des messages triés, le système a besoin d'un processus central pour organiser les données. Ce processus limite les capacités de scaling et réduit la disponibilité des services, car il s'agit d'un point de défaillance unique.

Dans ces architectures, les agents de messagerie ont tendance à mettre en œuvre une logique supplémentaire, comme suivre l'abonné qui a reçu tel message et surveiller la charge des abonnés. Les abonnés ont tendance à être simplement réactifs. Ils ne possèdent pas de connaissance du système global et exécutent simplement une fonction à la réception d'un message. Ces types d'architectures sont appelés pipelines intelligents (système de file d'attente de messages) et points de terminaison incongrus (abonné).

Transport Pub/Sub

À l'instar des systèmes de messagerie, les systèmes de diffusion d'événements acheminent également les messages d'un système source vers des systèmes de destination découpés. Cependant, plutôt que d'envoyer chaque message vers une file d'attente ciblée par processus, les systèmes basés sur des événements ont tendance à publier des messages dans un sujet partagé, puis un ou plusieurs destinataires s'abonnent à ce sujet pour écouter des informations pertinentes.

Le schéma suivant montre comment différents messages sont émis par un éditeur en amont dans un seul sujet, puis sont acheminés vers l'abonné en aval approprié :

Les messages d'un éditeur sont transférés vers un sujet unique pour tous les abonnés

Le terme pubsub se base sur ce modèle de publication et d'abonnement. Ce modèle constitue également la base du produit Google Cloud appelé Pub/Sub. Dans ce document, pubsub fait référence au modèle et Pub/Sub fait référence au produit.

Dans le modèle Pub/Sub, le système de messagerie n'a besoin de connaître aucun des abonnés. Il ne suit pas les messages reçus et ne gère pas la charge sur le processus de consommation. Au lieu de cela, les abonnés suivent les messages reçus et sont responsables de la gestion automatique des niveaux de charge et du scaling.

Un avantage important est que, lorsque vous rencontrez de nouvelles utilisations des données du modèle Pub/Sub, vous n'avez pas besoin de mettre à jour le système d'origine pour publier des nouvelles files d'attente ou dupliquer des données. À la place, vous associez votre nouveau consommateur à un nouvel abonnement sans aucun impact sur le système existant.

Les appels dans les systèmes de diffusion d'événements sont presque toujours asynchrones. Ils envoient des événements et n'attendent aucune réponse. Les événements asynchrones permettent de bénéficier d'options de scaling plus importantes, tant pour le producteur que pour les consommateurs. Toutefois, ce modèle asynchrone peut créer des défis si vous vous attendez à obtenir des garanties en matière d'ordre des messages FIFO.

Données en file d'attente de messages

Les données transmises entre les systèmes des systèmes de file d'attente de messages et celles des systèmes Pub/Sub sont généralement appelées messages dans les deux contextes. Cependant, le modèle dans lequel ces données sont présentées est différent. Dans les systèmes de files d'attente de messages, les messages reflètent une commande destinée à modifier l'état des données en aval. Si vous examinez les données pour des systèmes de file d'attente de messages sur site, l'éditeur peut indiquer explicitement ce que le consommateur doit faire. Par exemple, un message d'inventaire peut indiquer les éléments suivants :

<m:SetInventoryLevel>
    <inventoryValue>3001</inventoryValue>
</m: SetInventoryLevel>

Dans cet exemple, le producteur indique au consommateur qu'il doit définir le niveau d'inventaire sur 3001. Cette approche peut s'avérer difficile, car le producteur doit comprendre la logique métier de chaque consommateur et créer des structures de messages distinctes pour différents cas d'utilisation. Ce système de mise en file d'attente de messages était une pratique courante avec les grands monolithes que la plupart des entreprises ont mis en œuvre. Toutefois, si vous souhaitez aller plus vite, évoluer à plus grande échelle et innover plus que jamais, ces systèmes centralisés peuvent devenir un goulot d'étranglement, car le changement est risqué et lent.

Ce modèle présente également des défis opérationnels. Lorsque des données incorrectes, des enregistrements en double ou d'autres problèmes surviennent et doivent être corrigés, ce modèle de messagerie pose un problème sérieux. Par exemple, si vous devez restaurer le message utilisé dans l'exemple précédent, vous ne savez pas comment définir la valeur corrigée, car vous n'avez pas de référence à l'état précédent. Vous ne savez pas si la valeur d'inventaire était 3000 ou 4000 avant l'envoi de ce message.

Données pubsub

Les événements constituent un autre moyen d'envoyer des données de message. Les systèmes basés sur des événements se distinguent des événements qui se sont produits plutôt que des résultats qui devraient se produire. Au lieu d'envoyer des données indiquant l'action à effectuer par le consommateur, les données sont axées sur les détails de l'événement réel produit. Vous pouvez mettre en œuvre des systèmes pilotés par des événements sur diverses plates-formes, mais ils le sont souvent sur des systèmes basés sur Pub/Sub.

Par exemple, un événement d'inventaire peut se présenter comme suit :

{ "inventory":-1 }

Les données d'événement précédentes indiquent qu'un événement s'est produit. Cela a entraîné une diminution de 1 de l'inventaire. Les messages se concentrent sur l'événement qui s'est produit dans le passé et non de changement d'état à l'avenir. Les éditeurs peuvent envoyer des messages de manière asynchrone, ce qui facilite l'évolutivité des systèmes basés sur des événements que les modèles de file d'attente. Dans le modèle Pub/Sub, vous pouvez dissocier la logique métier afin que le producteur n'ait besoin que de comprendre les actions qui y sont effectuées et n'a pas besoin de comprendre les processus en aval. Les abonnés à ces données peuvent choisir la meilleure façon de gérer les données qu'ils reçoivent. Étant donné que ces messages ne sont pas des commandes impératives, l'ordre des messages devient moins important.

Ce modèle facilite l'annulation des modifications. Dans cet exemple, aucune information supplémentaire n'est nécessaire, car vous pouvez annuler la valeur d'inventaire pour la déplacer dans le sens opposé. Les messages en retard ou dans le désordre ne sont plus un problème.

Comparaison de modèles

Dans ce scénario, votre inventaire comporte quatre articles pour un même produit. Un client renvoie un compte du produit, et le client suivant en achète trois. Dans ce scénario, supposons que le message du produit renvoyé a été retardé.

Le tableau suivant compare le niveau d'inventaire du modèle de file d'attente de messages qui reçoit le nombre d'articles dans l'ordre approprié avec le même modèle recevant le nombre d'articles dans le désordre :

File d'attente de messages (dans le bon ordre) File d'attente de messages (dans le désordre)
Inventaire initial : 4 Inventaire initial : 4
Message 1 : setInventory(5) Message 2 : setInventory(2)
Message 2 : setInventory(2) Message 1 : setInventory(5)
Niveau d'inventaire : 2 Niveau d'inventaire : 5

Dans le modèle de file d'attente de messages, l'ordre de réception des messages est important, car il contient la valeur précalculée. Dans cet exemple, si les messages arrivent dans le bon ordre, le niveau d'inventaire est 2. Cependant, si les messages arrivent dans le désordre, le niveau d'inventaire est défini sur 5, ce qui est inexact.

Le tableau suivant compare le niveau d'inventaire du système basé sur pubsub qui reçoit le nombre d'articles dans l'ordre approprié avec le même système recevant le nombre d'articles dans le désordre :

Pubsub (dans le bon ordre) Pub/Sub (dans le désordre)
Inventaire initial : 4 Inventaire initial : 4
Message 2 : "inventory":-3 Message 1 : "inventory":+1
Message 1 : "inventory":+1 Message 2 : "inventory":-3
Niveau d'inventaire : 2 Niveau d'inventaire : 2

Dans le système basé sur Pub/Sub, l'ordre des messages n'a pas d'importance, car il est informé par les services qui produisent des événements. Quel que soit l'ordre dans lequel les messages arrivent, le niveau d'inventaire est exact.

Le schéma suivant montre comment, dans le modèle de file d'attente de messages, la file d'attente exécute des commandes indiquant à l'abonné comment l'état doit changer dans le modèle pubsub, et les abonnés réagissent aux données d'événement indiquant ce qui s'est passé dans l'éditeur :

Exemple de règlement comparant la réaction aux commandes à une réaction aux événements.

Mettre en œuvre des architectures basées sur les événements

Plusieurs concepts doivent être pris en compte lors de la mise en œuvre d'architectures basées sur des événements. Les sections suivantes présentent certains de ces sujets.

Garanties de distribution

Un concept qui revient lors d'une discussion sur le système est la fiabilité des garanties de distribution des messages. Différents fournisseurs et systèmes peuvent offrir des niveaux de fiabilité différents. Il est donc important de comprendre les variations.

Le premier type de garantie pose une question simple : un message est-il assuré d'être distribué ? C'est ce que l'on appelle une distribution de type "au moins une fois". Bien que le message soit distribué au moins une fois, il peut être envoyé plusieurs fois.

La livraison de type "pas plus qu'une fois" constitue un autre type de garantie. Avec la distribution de type "pas plus qu'une fois", le message n'est distribué qu'une seule fois, mais rien ne garantit que le message sera réellement distribué.

La dernière variante des garanties de livraison est la diffusion "exactement une fois". Dans ce modèle, le système envoie une seule et unique copie du message qui est garanti.

Ordre et doublons

Dans les architectures sur site, les messages suivent souvent un modèle FIFO. Pour atteindre ce modèle, un système de traitement centralisé gère le séquencement des messages pour garantir un ordre précis. Les messages classés génèrent des difficultés, car pour tous les messages ayant échoué, tous les messages doivent être envoyés de nouveau dans l'ordre. Tout système centralisé peut devenir un défi de disponibilité et d'évolutivité. Il n'est généralement pas possible de procéder au scaling d'un système central qui gère l'ordre en ajoutant des ressources à une machine existante. Avec un seul système qui gère la commande, tous les problèmes de fiabilité affectent l'ensemble du système plutôt que uniquement cette machine.

Les services de messagerie hautement évolutifs et disponibles utilisent souvent plusieurs systèmes de traitement pour s'assurer que les messages sont distribués au moins une fois. Avec de nombreux systèmes, la gestion de l'ordre des messages n'est pas garantie.

Les architectures basées sur des événements ne reposent pas sur l'ordre des messages et peuvent tolérer des messages en double. Si un ordre est nécessaire, les sous-systèmes peuvent mettre en œuvre des techniques d'agrégation et de fenêtrage. Cependant, cette approche sacrifie l'évolutivité et la disponibilité dans ce composant.

Techniques de filtrage et de déploiement

Étant donné qu'un flux d'événements peut contenir des données pouvant ou non être requises par chaque abonné, il est souvent nécessaire de limiter les données reçues par un abonné donné. Il existe deux modèles pour gérer cette exigence : les filtres d'événements et les déploiements d'événements.

Le schéma suivant illustre un système basé sur des événements avec des filtres d'événements pour les messages reçus par les abonnés :

Modèle basé sur les événements avec un filtre d'événement filtrant les messages reçus par les abonnés

Dans le schéma précédent, les filtres d'événement utilisent des mécanismes de filtrage qui limitent les événements qui atteignent l'abonné. Dans ce modèle, un seul sujet contient toutes les variantes d'un message. Plutôt qu'un abonné lit chaque message et vérifie le cas échéant, la logique de filtrage du système de messagerie évalue le message, ce qui le reste des autres abonnés.

Le diagramme suivant montre une variante de la structure de filtre d'événements appelée déploiement d'évènements qui utilise plusieurs sujets :

Modèle basé sur les événements avec déploiement des événements qui republie les messages sur les sujets

Dans le diagramme précédent, le sujet principal contient toutes les variantes d'un message, mais un mécanisme de déploiement d'événements republie les messages sur des sujets liés à ce sous-ensemble d'abonnés.

Files d'attente de messages non traités

Même dans les meilleurs systèmes, des défaillances peuvent se produire. Les files d'attente de messages non traités sont une technique permettant de traiter ce type d'échec. Dans la plupart des architectures basées sur les événements, le système de messagerie continue de fournir un message à un abonné jusqu'à ce qu'il soit confirmé par celui-ci.

Si un message présente un problème (des caractères non valides, par exemple), l'abonné risque de ne pas pouvoir le reconnaître. Le système peut ne pas gérer le scénario, voire même arrêter le processus.

Les systèmes relancent généralement les messages sans accusé de réception ou erronés. Si un message non valide n'est pas reçu après un laps de temps prédéterminé, le message arrive à expiration et est supprimé du sujet. D'un point de vue opérationnel, il est utile d'examiner les messages au lieu de les supprimer. C'est là qu'interviennent les files d'attente de messages non traités. Plutôt que de supprimer le message du sujet, le message est déplacé vers un autre sujet, où il peut être traité ou examiné pour comprendre la raison de l'erreur.

Historique de la diffusion et rediffusion

Les flux d'événements sont des flux de données en continu. L'accès à ces données de l'historique est utile. Vous voudrez peut-être savoir comment un système est parvenu à un état donné. Des questions liées à la sécurité nécessitent un audit des données. La capacité à capturer un journal historique des événements est essentielle dans le fonctionnement à long terme d'un système basé sur les événements.

Les données d'historique d'événements sont couramment utilisées avec un système de répétition. Les rediffusions sont utilisées à des fins de test. En relançant les données d'événement de la production dans d'autres environnements tels que la phase et le test, vous pouvez valider de nouvelles fonctionnalités sur des ensembles de données réels. Vous pouvez également relire les données de l'historique pour récupérer un état défaillant. Si un système tombe en panne ou perd des données, les équipes peuvent revoir l'historique des événements à partir d'un point connu, et le service peut reconstruire l'état qu'il a perdu.

La capture de ces événements dans des files d'attente basées sur des journaux ou des flux de journalisation est également utile lorsque les abonnés ont besoin d'accéder à une séquence d'événements à différents moments. Les flux de journalisation sont visibles dans des systèmes avec des fonctionnalités hors connexion. À l'aide de l'historique de votre flux, vous pouvez traiter les dernières entrées en lisant le flux à partir du pointeur last-read.

Vues des données : en temps réel et en quasi-temps réel

Avec toutes les données circulant via les systèmes, il est important de pouvoir les utiliser. Il existe de nombreuses techniques permettant d'accéder à ces flux d'événements et de les utiliser, mais un cas d'utilisation courant consiste à comprendre l'état général des données à un moment spécifique. Il s'agit souvent de questions axées sur le calcul, telles que "nombre" ou "niveau actuel", qui peuvent être utilisées par d'autres systèmes ou pour la consommation humaine. Plusieurs mises en œuvre peuvent répondre à ces questions :

  • Un système en temps réel peut s'exécuter en continu et suivre l'état actuel. Cependant, avec le système ne disposant que d'un calcul en mémoire, les temps d'arrêt définissent le calcul sur zéro.
  • Le système peut calculer des valeurs à partir de la table de l'historique pour chaque requête, mais cela peut poser problème, car il peut être impossible de calculer des valeurs pour chaque requête alors que les données augmentent.
  • Le système peut créer des instantanés des calculs à des intervalles spécifiques, mais l'utilisation uniquement des instantanés ne reflète pas les données en temps réel.

Un modèle utile à mettre en œuvre est une architecture Lambda dotée de capacités en quasi-temps réel et en temps réel. Par exemple, une page produit sur un site de commerce électronique peut utiliser des vues en quasi-temps réel des données d'inventaire. Lorsque les clients passent des commandes, un service en temps réel est utilisé pour garantir des mises à jour de l'état des secondes des données d'inventaire. Pour mettre en œuvre ce modèle, le service répond aux requêtes en quasi-temps réel provenant d'une table d'instantanés contenant des valeurs calculées sur un intervalle donné. Une requête en temps réel utilise à la fois la table d'instantanés et les valeurs de la table d'historique depuis le dernier instantané pour obtenir l'état actuel exact. Ces vues matérialisées des flux d'événements fournissent des données exploitables pour alimenter des processus métier réels.

Étape suivante