Remarque
L’accès à cette page nécessite une autorisation. Vous pouvez essayer de vous connecter ou de modifier des répertoires.
L’accès à cette page nécessite une autorisation. Vous pouvez essayer de modifier des répertoires.
Concevoir des consommateurs de messages afin que le traitement du même message plusieurs fois ait le même effet que le traitement d’une seule fois. Les systèmes de messagerie qui garantissent une remise au moins une fois peuvent remettre le même message plusieurs fois. Sans résilience contre les doublons, le retraitement d’un message peut créer des enregistrements en double, facturer deux fois un client ou avoir d’autres effets indésirables.
Contexte et problème
Les applications distribuées échangent généralement via un répartiteur de messages au lieu d’appels synchrones directs. La plupart des répartiteurs, notamment Azure Service Bus, Azure Event Hubs, Apache Kafka et RabbitMQ, fournissent au moins une livraison une fois. Cette garantie garantit qu’un message atteint un consommateur même en cas d’échec, mais cela signifie également que le répartiteur peut remettre le même message plusieurs fois.
Les doublons proviennent de plusieurs sources :
Nouvelles tentatives de producteur
Un producteur envoie un message, ne reçoit pas d’accusé de réception en raison d’une erreur réseau temporaire ou d’un délai d’expiration, et envoie à nouveau le message. Le répartiteur contient maintenant deux copies même si l’envoi a réussi la première fois.
Redelivery après un accusé de réception manquant
Un consommateur reçoit et traite un message, mais ne le reconnaît pas, car le consommateur se bloque, le verrou expire ou l’accusé de réception est perdu. Le répartiteur suppose que le message n’a pas été traité et le remet à nouveau.
Défaillances du consommateur en cours de traitement
Un consommateur termine l’écriture d’une base de données, mais se bloque avant qu’il ne reconnaisse le message. Lorsqu’une autre instance récupère le message redélisé, elle répète l’écriture.
Une livraison exactement une fois sur un système distribué n’est pas pratique à garantir. Même les répartiteurs qui prétendent exactement une fois la sémantique garantissent uniquement les opérations qu’ils contrôlent directement, telles que la remise de messages aux consommateurs ou l’écriture de données dans le répartiteur. Ils ne peuvent pas garantir les effets secondaires externes que les consommateurs effectuent dans d’autres systèmes. La solution durable n’est pas d’éliminer la livraison en double. C’est de rendre le consommateur tolérant. Lorsque vous combinez au moins une livraison une fois avec un consommateur qui ignore les doublons, vous obtenez un traitement exactement une fois.
Solution
Faites en sorte que le consommateur soit idempotent en conservant un enregistrement des messages traités et en ignorant tout message qu’il a vu précédemment. Le consommateur clé cette décision sur un identificateur stable qui survive à redelivery, vérifie un magasin persistant pour déterminer si cet identificateur a déjà été traité et traite le message ou l’ignore en tant que doublon.
Les étapes suivantes décrivent le flux principal :
- Lisez le message et extrayez sa clé de déduplication.
- Vérifiez le magasin de déduplication pour cette clé.
- Si la clé existe, traitez le message comme un doublon. Reconnaissez-le et arrêtez-le, en retournant éventuellement le résultat enregistré précédemment.
- Si la clé n’existe pas, traitez le message et enregistrez la clé dans une seule opération atomique, puis reconnaissez le message.
Choisir une clé de déduplication stable
La clé doit identifier de manière unique et cohérente le message logique dans chaque redelivery. Utilisez un identificateur de message attribué par le producteur ou une clé d’idempotency au niveau de l’entreprise qui identifie l’opération logique spécifique, et non un contexte de corrélation partagé que plusieurs messages peuvent porter. Dans Azure Service Bus, la MessageId propriété sert cet objectif, car elle identifie de manière unique le message et sa charge utile. N’utilisez CorrelationId pas la clé, car elle regroupe les messages associés, tels qu’une demande et ses réponses. Pour les événements qui suivent la spécification CloudEvents, la combinaison des source attributs et id des attributs identifie de façon unique un événement et reste stable dans les redeliveries.
Ne clé pas sur les identificateurs au niveau du transport que le répartiteur régénère sur redelivery ou sur les valeurs dérivées des tentatives de remise, car ces valeurs changent entre les doublons et la détection de défaite. Évitez également de dériver la clé à partir de champs volatiles tels que les horodatages de réception.
Lorsque plusieurs consommateurs indépendants traitent le même canal, comme plusieurs abonnés dans une conception de publication-abonnement, chaque consommateur traite légitimement sa propre copie d’un message et doit suivre indépendamment l’achèvement du traitement des messages. Si ces consommateurs partagent un magasin de déduplication, key the record on a composite of the consumer identity and the message identity. Un magasin clé sur l’identité de message seul permet au premier consommateur de supprimer le traitement pour tous les autres.
Décider où stocker les clés traitées
Vous avez deux options courantes :
Table de déduplication dédiée. Le consommateur gère une table distincte, parfois appelée boîte de réception, qui contient une ligne par clé traitée. Cette approche conserve les préoccupations de déduplication distinctes des données métier et fonctionne bien lorsque de nombreux types de messages partagent un mécanisme.
Entité métier elle-même. Le consommateur stocke la clé sur l’enregistrement que le message crée ou met à jour. Cette approche évite une table distincte, mais couple la déduplication à la forme des données métier.
Valider le marqueur et les effets secondaires atomiquement
Le flux check-then-process a une fenêtre d’échec. Si le consommateur traite le message, puis enregistre la clé dans une étape distincte, un blocage entre les deux opérations laisse les effets secondaires appliqués, mais la clé non enregistrée, de sorte que la remise suivante retraite le message.
Résolvez cette fenêtre d’échec en écrivant le marqueur de déduplication et les effets secondaires métier dans la même transaction. Lorsque les deux validations sont réunies ou pas du tout, une redelivery recherche le marqueur validé et ignore, ou ne trouve aucun marqueur, car la transaction a été restaurée et retraite en toute sécurité. Cette variante transactionnelle est le modèle de boîte de réception, et il s’agit du compagnon côté consommation du modèle de boîte de réception transactionnelle côté produit.
Protéger contre les doublons simultanés
Sous une remise au moins une fois avec plusieurs consommateurs concurrents, deux instances peuvent recevoir des copies du même message en même temps. Les deux peuvent passer la vérification de l’existence avant les validations, de sorte que la vérification seule n’empêche pas le double traitement.
Appliquez la correction au niveau du magasin de données au lieu de la logique de l’application :
Utilisez une contrainte unique sur la clé de déduplication. Les deux transactions tentent d’insérer la clé, mais une seule réussit. L’autre échoue la contrainte et traite le message comme un doublon. Cette approche rend la base de données l’arbitre unique de la course.
Évitez les courses check-then-set dans les caches. Un modèle qui vérifie une clé, puis le définit sur deux opérations distinctes a une fenêtre qui permet aux nouvelles tentatives simultanées de revendiquer la clé. Utilisez une écriture conditionnelle atomique, telle qu’une insertion qui échoue en conflit ou une opération set-if-absent, afin que la revendication de la clé soit une seule étape atomique.
Gérer les effets secondaires qui ne peuvent pas joindre la transaction
Certains processus ne peuvent pas participer à la transaction de base de données du consommateur, telles que l’appel d’une API tierce ou l’écriture dans un magasin externe. Pour ces processus, utilisez une approche en deux phases :
- Enregistrez la clé avec un état en cours avant d’effectuer l’action externe.
- Effectuez le processus.
- Mettez à jour l’enregistrement pour terminer et stocker le résultat.
Sur redelivery, un enregistrement terminé vous permet d’ignorer la répétition de l’appel. Un enregistrement en cours signale qu’une tentative précédente a peut-être été partiellement terminée ou qu’elle est en cours d’exécution par un autre consommateur.
Problèmes et considérations
Tenez compte des points suivants lorsque vous décidez comment implémenter ce modèle :
Préférez les opérations idempotentes naturellement. Certaines opérations sont intrinsèquement idempotentes et n’ont pas besoin de déduplication de comptabilité. Un upsert keyed sur un identificateur d’entreprise, un écriture qui définit une valeur absolue plutôt qu’un incrément, ou un http
PUTsur un identificateur de ressource produit le même résultat qu’il s’exécute une ou plusieurs fois.Vous pouvez parfois effectuer une opération naturellement idempotente par le biais d’un transfert d’état porté par les événements, où le message porte l’état absolu résultant, tel que le nouvel état d’une commande, afin que le consommateur l’applique en tant qu’upsert au lieu d’une modification relative.
Conseil / Astuce
Conception pour la première idempotency naturelle, et ajouter des techniques de déduplication uniquement pour les opérations qui ne peuvent pas être rendues naturellement idempotentes.
Gérez le cycle de vie des enregistrements de déduplication. Les enregistrements de déduplication s’accumulent, sauf si vous les expirez. Conservez chaque enregistrement au moins tant que le répartiteur peut redéliver le message d’origine. Cette fenêtre dépend des tentatives de remise maximales du répartiteur, de son délai d’expiration de verrouillage ou de visibilité et du délai de vie du message. Définissez un délai de vie sur les enregistrements de déduplication qui dépassent cette fenêtre afin qu’une redelivery tardive trouve toujours son marqueur. La suppression d’enregistrements trop tôt ouvre la fenêtre pour les doublons. Comptez les messages qu’un opérateur resoumet à partir d’une file d’attente de lettres mortes, car une nouvelle soumission peut se produire longtemps après la fenêtre redelivery normale.
Utilisez une infrastructure de messagerie au lieu de la déduplication propagée manuellement. L’implémentation du magasin de déduplication, de la validation atomique et du nettoyage des enregistrements correctement est sujette aux erreurs. Les infrastructures basées sur les messages fournissent ce modèle en tant que fonctionnalité intégrée.
Par exemple, NServiceBus déduplique les messages entrants par leur identificateur de message et fournit une rétention et un nettoyage configurables pour les données de déduplication. La boîte de réception du consommateur MassTransit suit les messages reçus par leur identificateur de message pour fournir exactement un comportement consommateur une fois.
La déduplication du répartiteur réduit mais ne supprime pas la nécessité d’une logique consommateur idempotente. Certaines plateformes filtrent les doublons au niveau de la couche de transport. Azure Service Bus détection en double ignore les messages qui comportent une fenêtre de temps configurée
MessageId, ce qui supprime les doublons provoqués par les nouvelles tentatives d’envoi du producteur. Cette fonctionnalité fonctionne côté envoi et dans une fenêtre délimitée. Il n’empêche pas un consommateur de traiter le même message deux fois après une redelivery. Vous avez donc toujours besoin d’une logique consommateur idempotente. Traitez les fonctionnalités de plateforme comme une première couche de défense qui réduit le volume en double, et non comme un remplacement du modèle.Compte de l’ordre des messages. La déduplication supprime les doublons, mais ne garantit pas l’ordre. Si le consommateur dépend du traitement de la commande, combinez ce modèle avec un mécanisme de classement, tel que Azure Service Bus sessions de messages, ou incluez des données de séquence ou de version qui permettent au consommateur de rejeter les messages obsolètes.
Instrument d’observabilité. Émettez la clé de déduplication et un identificateur de corrélation dans les journaux structurés et suivez une métrique pour les doublons détectés. Un taux de doublon croissant peut indiquer une mauvaise configuration du producteur, une fenêtre d’accusé de réception ou de verrouillage sous-volumineuse, ou des consommateurs défectueux. Utilisez le suivi et la corrélation de bout en bout pour suivre un message entre les services.
Propagez l’idempotency aux appels en aval. Rendre un idempotent consommateur ne protège pas les services qu’il appelle. Lorsqu’un consommateur appelle des services en aval dans le cadre du traitement, propagez la clé d’idempotency afin que chaque niveau puisse dédupliquer son propre travail.
Quand utiliser ce modèle
Utilisez ce modèle dans les situations suivantes :
Vous consommez des messages d’un répartiteur qui fournit au moins une remise une fois, qui est la valeur par défaut pour la plupart des répartiteurs.
Le retraitement d’un message produit des résultats incorrects, tels que les transactions financières en double, la création de ressources en double ou les notifications répétées.
Plusieurs consommateurs concurrents traitent le même canal, ce qui rend probable la livraison en double simultanée.
Ce modèle peut ne pas convenir lorsque :
Chaque opération effectuée par le consommateur est déjà naturellement idempotente, donc le retraitement est inoffensif et la déduplication de la comptabilité ajoute des coûts sans avantage.
La charge de travail peut tolérer les effets d’un traitement occasionnel en double, et le coût d’un magasin de déduplication dépasse l’impact d’un doublon.
Traitement Idempotent au-delà de la messagerie
Ce modèle applique l’idempotency aux consommateurs de messages, mais le traitement idempotent est un principe de fiabilité plus large. Toute opération pouvant s’exécuter plusieurs fois sur une tâche identique en bénéficie. Ce principe inclut les transformations d’extraction, de transformation, de chargement (ETL) qui retraitent les données relectées, le traitement de flux qui reprend à partir d’un point de contrôle, des travaux planifiés qui chevauchent ou redémarrent, et des points de terminaison Webhook ou HTTP qui reçoivent des remises en double.
Dans chaque cas, la même technique de base s’applique :
- Identifiez l’unité de travail avec une clé stable.
- Enregistrez ce que vous avez déjà traité.
- Ignorez ou absorbez les doublons afin que la répétition du travail ne modifie pas le résultat.
Les mécanismes de cet article, tels que les clés stables, les marqueurs atomiques et les contraintes uniques, sont transférés vers ces contextes même lorsqu’aucun répartiteur de messages n’est impliqué.
Conception de la charge de travail
Évaluez comment utiliser le modèle consommateur Idempotent dans la conception d'une charge de travail pour répondre aux objectifs et principes abordés dans les piliers du Azure Well-Architected Framework. Le tableau suivant fournit des conseils sur la façon dont ce modèle prend en charge les objectifs de chaque pilier.
| Pilier | Comment ce modèle soutient les objectifs des piliers. |
|---|---|
| Les décisions de conception de fiabilité aident votre charge de travail à devenir résiliente au dysfonctionnement et à s’assurer qu’elle se rétablit dans un état entièrement opérationnel après une défaillance. | Ce modèle permet à une charge de travail d’utiliser une remise au moins une fois et de nouvelles tentatives sécurisées sans endommager les données, ce qui transforme la remise en double d’un risque de correction en condition tolérée. - RE :07 Autopréservation - Gérer les erreurs temporaires |
Si ce modèle introduit des compromis au sein d’un pilier, considérez-les contre les objectifs des autres piliers.
Exemple
L’exemple suivant montre un consommateur idempotent qui traite les commandes de Azure Service Bus et conserve l’état dans Azure Cosmos DB pour NoSQL.
Un producteur définit l’Service Bus MessageId sur un identificateur de commande au niveau de l’entreprise. Le consommateur reçoit des messages en mode PeekLock , ce qui redélise un message si le consommateur ne le termine pas pendant la durée de verrouillage. Les partitions de conteneur Azure Cosmos DB du consommateur sur l'identificateur de commande (/orderId) et définissent le document id sur le même identificateur d'ordre. Par conséquent, chaque copie d'un ordre donné se résout sur la même partition logique et l'enregistrement d'ordre lui-même sert de marqueur de déduplication.
Le consommateur traite chaque message comme suit :
- Lisez le message et utilisez-le
MessageIdcomme clé de déduplication. - Créez le document d’ordre avec
idet la clé de partition définie sur l’identificateur de commande. - Si la création réussit, complétez le message afin que Service Bus le supprime de la file d’attente.
- Si la création échoue avec un état HTTP 409 (conflit), car un document avec celui-ci
idexiste déjà, lisez le document existant et comparez-le au message actuel. Si un hachage de requête stocké ou des champs métier immuables correspondent, traitez le message comme un doublon, terminez-le et ignorez le traitement. S’ils ne correspondent pas, le producteur a peut-être réutilisé l’identificateur pour un contenu différent ou les détails de la commande ont peut-être changé depuis son premier traitement, de sorte que la lettre morte le message ou déclencher une alerte au lieu de l’ignorer silencieusement. - Si le traitement échoue pour une raison temporaire, abandonnez le message de sorte que Service Bus le rééliver, ou laissez le verrou expirer afin qu’un autre consommateur le reçoive.
L’opération de création est atomique. Elle sert donc de vérification de déduplication et d’écriture. Deux consommateurs qui reçoivent des copies du même message ne peuvent pas créer la commande. Une création gagne, et l’autre retourne un conflit et ignore en toute sécurité son doublon.
Lorsque le traitement doit écrire plusieurs documents, utilisez un lot transactionnel qui inclut à la fois le document de déduplication et les documents métier au sein de la même clé de partition. Étant donné qu’un lot transactionnel fonctionne dans une seule partition logique, choisissez une clé de partition que tous les documents d’un partage de messages. Le lot valide tous les documents ensemble ou aucun du tout, de sorte qu’un blocage entre le traitement et l’accusé de réception ne peut pas laisser le marqueur de déduplication et les données métier hors synchronisation. Un lot qui tente de créer un document qui existe déjà retourne un état 409 (Conflit), qui identifie le doublon.
Pour rendre ce consommateur résilient par rapport aux nouvelles tentatives d’envoi en double, activez également la détection des doublons dans la file d’attente. La détection dupliquée supprime les envois répétés dans sa fenêtre d’historique, et le consommateur idempotent gère les doublons qui se trouvent en dehors de cette fenêtre ou qui résultent d’une redelivery.
Étape suivante
- Les options de messagerie asynchrone dans Azure décrivent les choix d’infrastructure de messagerie qui déterminent les garanties de remise et les exigences de gestion en double.
Ressources associées
Le modèle de boîte de réception transactionnelle est le côté éditeur de ce modèle. Il publie de manière fiable les messages en les commitant dans la même transaction que les données métier.
Le modèle de nouvelle tentative permet aux applications de gérer les erreurs temporaires en effectuant des opérations de nouvelle tentative, ce qui rend le traitement idempotent nécessaire, car les nouvelles tentatives peuvent entraîner une remise en double.
La conception résiliente Azure Event Hubs et Azure Functions applique ce modèle aux fonctions qui Azure Event Hubs déclencheurs, y compris les techniques de déduplication pour les flux d’événements.
La conception de Azure Functions pour les entrées identiques fournit des conseils pour la création de fonctions idempotentes qui tolèrent les appels en double.