Une architecture pilotée par les événements se compose de producteurs d’événements qui génèrent un flux d’événements, des consommateurs d’événements qui écoutent ces événements et des canaux d’événements qui transfèrent des événements des producteurs aux consommateurs.
Les événements étant remis en quasi-temps réel, les consommateurs peuvent y répondre immédiatement à mesure qu’ils se produisent. Les producteurs sont dissociés des consommateurs (un producteur ne peut pas identifier les consommateurs qui sont à l’écoute). Les consommateurs sont aussi dissociés les uns des autres, et chaque consommateur voit tous les événements. Il s'agit d'une différence notable par rapport au modèle des consommateurs concurrents, où les consommateurs extraient les messages d'une file d’attente et où un message est traité une seule fois (à condition qu'il n'y ait pas d'erreurs). Dans certains systèmes, comme IoT, les événements doivent être ingérés dans de très gros volumes.
Une architecture basée sur les événements peut utiliser un modèle de publication/abonnement (également appelé pub/sub) ou un modèle de flux d’événements.
Publication/abonnement : l’infrastructure de messagerie effectue le suivi des abonnements. Quand un événement est publié, elle communique l’événement à chaque abonné. Une fois l’événement reçu, il ne peut pas être relu et les nouveaux abonnés ne le voient pas.
Streaming d’événements : les événements sont écrits dans un journal. Les événements sont classés dans un ordre strict (au sein d’une partition) et sont durables. Les clients ne s’abonnent pas au flux, mais ils peuvent lire n’importe quelle partie du flux. Il revient au client d’avancer sa position dans le flux. Cela signifie qu’un client peut se joindre à tout moment et relire les événements.
Du côté du consommateur, il existe quelques variantes courantes :
Traitement des événements simples : un événement déclenche immédiatement une action dans le consommateur. Par exemple, vous pouvez utiliser Azure Functions avec un déclencheur Service Bus, de telle sorte qu’une fonction s’exécute chaque fois qu’un message est publié dans une rubrique Service Bus.
Corrélation entre les événements de base. Un consommateur doit traiter un petit nombre d’événements commerciaux discrets, généralement corrélés par un identifiant, en conservant certaines informations des événements antérieurs pour les utiliser lors du traitement des événements ultérieurs. Ce modèle est pris en charge par les bibliothèques telles que NServiceBus et MassTransit.
Traitement des événements complexes : un consommateur traite une série d’événements, à la recherche de modèles dans les données d’événement, en s’appuyant sur une technologie telle que Azure Stream Analytics. Par exemple, vous pouvez agréger les relevés d’un appareil intégré dans une fenêtre de temps et générer une notification si la moyenne mobile dépasse un certain seuil.
Traitement des flux d’événements : utilisez une plateforme de diffusion de données, telle qu’Azure IoT Hub ou Apache Kafka, comme pipeline pour ingérer les événements et les transmettre aux processeurs de flux. Les processeurs de flux agissent de façon à traiter ou transformer le flux. Il peut exister plusieurs processeurs de flux pour différents sous-systèmes de l’application. Cette approche est parfaitement adaptée aux charges de travail IoT.
La source des événements peut être extérieure au système. Il peut s’agir par exemple des appareils physiques d’une solution IoT. Dans ce cas, le système doit pouvoir ingérer les données selon le volume et le débit imposés par la source de données.
Il existe deux approches principales pour structurer les charges utiles d’événement. Lorsque vous contrôlez vos consommateurs d’événements, prenez cette décision de structure de charge utile par consommateur ; mélange d’approches selon les besoins au sein d’une seule charge de travail.
Inclusion de tous les attributs requis dans la charge utile : cette approche est utilisée lorsque vous souhaitez que les consommateurs disposent de toutes les informations disponibles sans avoir besoin d’interroger une source de données externe. Toutefois, il peut entraîner des problèmes de cohérence des données en raison de plusieurs systèmes d’enregistrement, en particulier après les mises à jour. La gestion des contrats et le contrôle de version peuvent également devenir complexes.
Y compris uniquement les clés dans la charge utile : dans cette approche, les consommateurs récupèrent les attributs nécessaires, tels qu’une clé primaire, pour extraire indépendamment les données restantes d’une source de données. Bien que cette méthode offre une meilleure cohérence des données en raison d’un seul système d’enregistrement, elle peut être plus médiocre que la première approche, car les consommateurs doivent interroger fréquemment la source de données. Il y a moins de préoccupations concernant le couplage, la bande passante, la gestion des contrats ou le contrôle de version, car les événements sont plus petits et les contrats sont plus simples.
Dans le diagramme logique ci-dessus, chaque type de consommateur est représenté par un cadre unique. Dans la pratique, il est courant d’avoir plusieurs instances d’un même consommateur pour éviter que celui-ci devienne un point de défaillance unique dans le système. Plusieurs instances peuvent aussi s’avérer nécessaires pour gérer le volume et la fréquence des événements. De même, un même consommateur peut traiter les événements de plusieurs threads. Cela peut être une source de problèmes si les événements doivent être traités dans l'ordre ou s'ils nécessitent une sémantique « exactly-once » (exactement une fois). Consultez Réduire la coordination.
Il existe deux topologies principales dans de nombreuses architectures pilotées par les événements :
Topologie de répartiteur. Les composants diffusent des événements à l’ensemble du système, et les autres composants agissent sur l’événement ou l’ignorent. Cette topologie est utile lorsque le flux de traitement des événements est relativement simple. Il n'y a pas de coordination ou d'orchestration centrale, de sorte que cette topologie peut être très dynamique. Elle est fortement découplée, ce qui contribue à l'évolutivité, à la réactivité et à la tolérance aux pannes des composants. Aucun composant ne possède ou ne connaît l’état d’une transaction commerciale à plusieurs étapes, et les actions sont effectuées de manière asynchrone. Ainsi, les transactions distribuées sont risquées car il n'existe aucun moyen natif de les relancer ou de les rejouer. La gestion des erreurs et les stratégies d’intervention manuelle doivent être soigneusement prises en compte, car cette topologie peut être une source d’incohérence des données.
Topologie de médiateur. Cette topologie traite certaines des lacunes de la topologie de répartiteur. Il existe un médiateur d’événements qui gère et contrôle le flux des événements. Le médiateur d'événements maintient l'état et gère le traitement des erreurs et les capacités de redémarrage. Contrairement à la topologie de répartiteur, les composants diffusent des occurrences sous forme de commandes et uniquement vers des canaux désignés, généralement des files d’attente de messages. Ces commandes ne sont pas censées être ignorées par leurs consommateurs. Cette topologie offre un contrôle accrue, une meilleure gestion des erreurs distribuées et une meilleure cohérence des données. Cette topologie introduit un couplage accru entre les composants, et le médiateur d’événement pourrait devenir un goulot d’étranglement ou mettre en danger la fiabilité.
Quand utiliser cette architecture
- Plusieurs sous-systèmes doivent traiter les mêmes événements.
- Traitement en temps réel avec un décalage dans le temps minimal.
- Traitement des événements complexes, tel que les critères spéciaux ou l’agrégation dans des fenêtres de temps.
- Volume et vélocité élevés des données, par exemple IoT.
Avantages
- Les producteurs et les consommateurs sont dissociés.
- Aucune intégration point à point. Simplicité d’ajout de nouveaux consommateurs au système.
- Les consommateurs peuvent répondre immédiatement aux événements, dès leur arrivée.
- Hautement évolutif, élastique et distribué.
- Les sous-systèmes ont des vues indépendantes du flux d’événements.
Défis
Transmission non garantie.
Dans certains systèmes, surtout dans les scénarios IoT, il est essentiel de garantir la transmission des événements.
Traitement des événements dans l’ordre ou une seule fois.
Chaque type de consommateur s’exécute généralement dans plusieurs instances à des fins de résilience et de scalabilité. Cela peut être une source de problèmes si les événements doivent être traités dans l’ordre (dans un type de consommateur) ou si la logique de traitement des messages idempotents n’est pas implémentée.
Coordination des messages entre services.
Dans le cadre de processus métier, les services sont souvent amenés à publier des messages et à s’y abonner pour obtenir un résultat cohérent à l’échelle de toute une charge de travail. Il est possible d’utiliser des modèles de workflow tels que le modèle de chorégraphie et l’orchestration Saga pour gérer de manière fiable les flux de messages entre divers services.
Gestion des erreurs.
L’architecture événementielle utilise principalement une communication asynchrone. Un défi avec la communication asynchrone est la gestion des erreurs. Une façon de résoudre ce problème est d’utiliser un processeur de gestion des erreurs distinct. Ainsi, lorsque le consommateur d’événements rencontre une erreur, il envoie immédiatement et de manière asynchrone l’événement erroné au processeur de gestion des erreurs et continue. Le processeur de gestion des erreurs tente de corriger l’erreur et renvoie l’événement au canal d’ingestion d’origine. Mais si le processeur de gestion des erreurs échoue, il peut alors envoyer l’événement erroné à un administrateur pour une inspection plus approfondie. Si vous utilisez un processeur de gestion des erreurs, les événements erronés seront traités hors séquence lorsqu’ils seront resoumis.
Perte de données.
Un autre défi avec la communication asynchrone est la perte de données. Si l’un des composants se bloque avant de traiter et de remettre l’événement à son composant suivant, l’événement est supprimé et ne le rend jamais dans la destination finale. Pour réduire le risque de perte de données, conservez les événements en transit et supprimez ou supprimez les événements uniquement lorsque le composant suivant a reconnu la réception de l’événement. Ces fonctionnalités sont généralement appelées mode accusé de réception du client et support du dernier participant.
Implémentation d’un modèle de demande-réponse traditionnel.
Parfois, le producteur d’événements nécessite une réponse immédiate du consommateur d’événements, par exemple obtenir l’éligibilité d’un client avant de passer une commande. Dans l’architecture pilotée par les événements, la communication synchrone peut être obtenue via la messagerie de demande-réponse.
Ce modèle est généralement implémenté en utilisant plusieurs files d’attente : une file d’attente de requêtes et une file d’attente de réponse. Le producteur d’événements envoie une requête asynchrone à une file d’attente de requêtes, interrompt d’autres opérations sur cette tâche et attend une réponse dans la file d’attente de réponse ; transformer cela en processus synchrone. Les consommateurs d’événements traitent ensuite la demande et envoient la réponse par le biais d’une file d’attente de réponse. Cette approche utilise généralement un ID de session pour le suivi, afin que le producteur d’événements sache quel message dans la file d’attente de réponse est lié à la requête spécifique. La demande d’origine peut également spécifier le nom de la file d’attente de réponse, potentiellement éphémère, dans un en-tête de réponse ou un autre attribut personnalisé mutuellement convenu.
Maintien du nombre approprié d’événements.
La génération d’un nombre excessif d’événements affinés peut saturer et submerger le système, ce qui rend difficile l’analyse efficace du flux global des événements. Ce problème est aggravé lorsque les modifications doivent être restaurées. À l’inverse, la consolidation excessive des événements peut également créer des problèmes, ce qui entraîne un traitement et des réponses inutiles des consommateurs d’événements.
Pour obtenir le bon équilibre, tenez compte des conséquences des événements et indiquez si les consommateurs doivent inspecter les charges utiles des événements pour déterminer leurs réponses. Par exemple, si vous disposez d’un composant de vérification de conformité, il peut suffire de publier seulement deux types d’événements : conformes et non conformes. Cette approche permet à chaque événement d’être traité uniquement par les consommateurs pertinents, ce qui empêche le traitement inutile.
Considérations supplémentaires
- La quantité de données à inclure dans un événement peut être un facteur important qui affecte les performances et les coûts. Regrouper toutes les informations pertinentes nécessaires au traitement dans l’événement lui-même peut simplifier le code de traitement et éviter des recherches supplémentaires. Regrouper un minimum d’informations dans un événement, comme quelques identifiants seulement, réduit le temps et le coût de transport, mais nécessite le code de traitement pour rechercher toute information supplémentaire dont il a besoin. Pour plus d’informations à ce propos, consultez ce billet de blog.
- Bien qu’une requête soit visible uniquement par le composant de gestion des demandes, les événements sont souvent visibles par plusieurs composants d’une charge de travail, même si ces composants ne sont pas destinés à les consommer ou ne sont pas destinés à les consommer. En utilisant une mentalité de « violation de principe », gardez à l’esprit les informations que vous incluez dans les événements afin d’empêcher l’exposition involontaire d’informations.
- De nombreuses applications utilisent l’architecture basée sur les événements comme architecture principale ; Toutefois, cette approche peut être combinée avec d’autres styles architecturaux, ce qui entraîne des architectures hybrides. Les combinaisons courantes incluent les microservices et les canaux et les filtres. L’intégration de l’architecture pilotée par les événements améliore les performances du système en éliminant les goulots d’étranglement et en fournissant une pression arrière pendant les volumes de requêtes élevés.
- Les domaines spécifiques couvrent souvent plusieurs producteurs d’événements, consommateurs ou canaux d’événements. Les modifications apportées à un domaine particulier peuvent avoir un impact sur de nombreux composants.
Ressources associées
- Vidéo de discussion de la communauté sur les considérations relatives au choix entre la chorégraphie et l’orchestration.