Modèle de convoi séquentiel

Regroupez les messages associés par une clé de catégorie et traitez chaque groupe de manière séquentielle, un message à la fois, tout en traitant différents groupes en parallèle.

Ce modèle résout la tension entre la maintenance de la correction du premier entré et du premier sorti (FIFO) au sein de chaque groupe logique et le scale-out du traitement simultané entre les groupes. La conception garantit que les contraintes d’ordonnancement ne deviennent pas un goulot d’étranglement pour l’ensemble du système.

Contexte et problème

Les applications doivent souvent traiter les messages associés dans l’ordre dans lequel ils arrivent tout en effectuant un scale-out pour gérer une charge accrue. Dans une architecture distribuée, cette exigence est difficile à atteindre, car les workers extrayent indépendamment les messages d’une file d’attente partagée. Lorsque plusieurs processus entrent en concurrence pour les messages, comme dans le modèle Consommateurs concurrents, l’ordre des messages n’est plus garanti.

Considérez un système de suivi des commandes qui reçoit un flux d’opérations, comme la création d’une commande, l’ajout d’une transaction, la modification d’une transaction passée et la suppression d’une commande. Les opérations de chaque commande doivent être traitées dans l’ordre FIFO, car leur application hors séquence endommagerait l’état de l’ordre. Toutefois, la file d’attente entrante entrelace les opérations entre plusieurs commandes. Un seul consommateur qui impose un ordre global devient un goulot d’étranglement, et plusieurs consommateurs peuvent traiter les opérations d’une même commande hors séquence.

Les approches simples de ce problème se décomposent de différentes manières :

  • Consommateur unique. Un seul consommateur conserve l’ordre des messages, car il traite un message à la fois, mais il ne peut pas être mis à l’échelle pour gérer un débit accru.

  • Plusieurs consommateurs concurrents. Plusieurs consommateurs augmentent le débit en récupérant les messages en parallèle, mais ils perdent les garanties d’ordre au sein de chaque groupe. Deux processus peuvent récupérer des messages consécutifs pour la même commande et les traiter simultanément ou hors séquence, ce qui corrompt l’état de la commande.

Solution

Le modèle convoi séquentiel partitionne les messages associés en catégories et traite chaque catégorie de manière séquentielle, un message à la fois, tandis que les catégories sont traitées en parallèle.

Le modèle fonctionne en affectant à chaque message une clé de catégorie qui identifie le groupe auquel il appartient. Un répartiteur de messages utilise cette clé pour partitionner les messages en groupes logiques. Au sein de chaque groupe, le broker applique l’ordre FIFO afin qu’un consommateur qui verrouille un groupe reçoive les messages strictement dans l’ordre exact où ils ont été mis en file d’attente. Différents groupes peuvent être traités simultanément par différents consommateurs, de sorte que le système peut donc évoluer horizontalement d’un groupe à l’autre sans sacrifier l’ordre de traitement au sein d’un même groupe.

Sur Azure, Azure Service Bus sessions de messages fournissent une implémentation intégrée de ce modèle.

Le diagramme suivant montre le modèle de convoi séquentiel général.

Diagramme du modèle de convoi séquentiel. Il montre un producteur, une file d’attente centrale et trois consommateurs.

Dans la file d’attente, les messages pour différentes catégories peuvent être entrelacés, comme illustré dans le diagramme suivant.

Diagramme montrant quatre catégories de messages entrelacés dans une file d’attente unique. Chaque catégorie occupe sa propre voie horizontale.

Ce modèle offre plusieurs avantages clés :

  • Traitement ordonné par groupe. Les messages de chaque catégorie sont traités strictement dans l’ordre, ce qui empêche les situations de concurrence, les modifications d’état effectuées dans le désordre et le besoin de recourir à des mécanismes de réordonnancement.

  • Échelle horizontale entre les groupes. Chaque catégorie est une unité indépendante de concurrence. L’ajout de consommateurs augmente le débit proportionnellement au nombre de catégories actives, sans rompre les garanties d’ordre.

  • Découplage entre producteur et consommateur. Les producteurs mettent des messages en file d’attente sans savoir quel consommateur les traitera ni à quel moment. Les consommateurs sont évolutifs et remplaçables indépendamment.

Problèmes et considérations

Prenez en compte les points suivants lorsque vous choisissez comment implémenter ce modèle :

  • Unité de catégorie et d’échelle. Déterminer quelle propriété de vos messages entrants vous pouvez mettre à l’échelle. La clé de catégorie définit l’unité de parallélisme : chaque valeur de clé distincte devient un groupe processable indépendamment. Dans le scénario de suivi des commandes, cette propriété est l’ID de commande. Le choix d’une clé trop grossière (par exemple, un ID client unique pour toutes les commandes) limite le parallélisme, tout en choisissant une clé trop fine ne fournit pas d’avantage significatif de commande.

  • Limites de débit. Évaluez le débit de votre message cible. Étant donné que ce modèle applique un traitement séquentiel dans chaque catégorie, le débit par catégorie est limité par le temps de traitement d’un seul message. Optimisez le temps de traitement par message, par exemple en utilisant des E/S asynchrones ou en regroupant par lots les écritures en aval, car ce temps détermine directement le débit maximum pour chaque catégorie. Si votre exigence globale de débit est très élevée, vérifiez si l’ordre FIFO strict est nécessaire pour l’ensemble du cycle de vie des messages. Les alternatives incluent l’application d’un message de début et d’un message de fin entre crochets, ou le tri des messages par horodatage dans une fenêtre de traitement par lot, puis l’envoi du lot pour le traitement parallèle.

  • Fonctionnalités du service. Vérifiez si votre choix de répartiteur de messages prend en charge le traitement unique à temps des messages au sein d’une file d’attente ou d’une catégorie de file d’attente. Tous les services de messagerie n’offrent pas de verrouillage au niveau de la session ni de garanties FIFO au sein d’une partition. Si le répartiteur ne prend pas en charge cette fonctionnalité en mode natif, le consommateur doit implémenter sa propre logique de coordination, ce qui ajoute de la complexité et des risques de traitement en double, de messages manqués ou d’exécution hors commande. La prise en charge des sessions peut également restreindre le choix du niveau de messagerie ou du SKU, ce qui a une incidence sur le coût.

  • Évolutivité. Planifiez la façon dont vous allez ajouter de nouvelles catégories de messages au système. Le modèle doit prendre en compte la croissance de la cardinalité de catégorie sans nécessiter de changements structurels pour les consommateurs. Par exemple, supposons que le système de registre décrit précédemment soit spécifique à un client. Si vous devez intégrer un nouveau client, vous devez être en mesure d’ajouter un ensemble de processeurs de registre qui distribuent le travail par ID client sans reconcevoir la topologie de file d’attente.

  • Remise de messages hors commande. Les messages peuvent arriver dans le désordre en raison d’une latence réseau variable entre le producteur et le broker, avant que le séquencement de session du broker ne prenne effet. Envisagez d’utiliser des numéros de séquence pour vérifier l’ordre dans chaque catégorie. Vous pouvez également inclure un indicateur de fin de séquence dans le dernier message d’une transaction afin que les consommateurs puissent détecter lorsqu’une séquence est terminée.

  • Gestion des messages empoisonnés. Un message qui échoue à plusieurs reprises dans une session bloque tous les messages suivants dans cette session, car le modèle applique un ordre séquentiel strict. Concevez une stratégie pour détecter les messages défectueux, par exemple en suivant le nombre de tentatives de remise, et déplacez-les vers une file de messages morts après un nombre défini de tentatives, afin que les messages restants de la session puissent continuer à être traités.

  • Disponibilité du répartiteur. Le répartiteur de messages est une dépendance partagée pour toutes les catégories. Sa disponibilité et sa durabilité affectent directement les garanties de fiabilité du modèle. Évaluez les fonctionnalités de résilience au niveau du répartiteur, telles que les zones de disponibilité et la récupération d’urgence géographique en fonction des besoins et du budget de la charge de travail, car les configurations de durabilité supérieure augmentent généralement le coût.

  • Correction de la clé de producteur. Le modèle suppose que les producteurs définissent correctement la clé de catégorie (ID de session) sur chaque message. Si un producteur définit une clé incorrecte, accidentellement ou en raison d’un bogue, le message est acheminé vers la session incorrecte et endommage l’état de ce groupe. Vérifiez que les producteurs attribuent des clés de catégorie de manière cohérente et envisagez d’ajouter une logique de validation de clé au consommateur si la conséquence d’un message mal routé est grave.

  • Complexité opérationnelle. La surveillance du traitement par session ajoute une surcharge opérationnelle plus importante que celle du traitement standard d’une file d’attente. Les opérateurs ont besoin d’une visibilité sur les backlogs de session (le nombre de sessions actives et la profondeur des messages en attente dans chaque session) pour identifier les catégories qui sont en retard. Les sessions à lettres mortes nécessitent un flux de travail de surveillance et de correction distinct pour examiner les messages ayant échoué, résoudre la cause racine et relire les messages corrigés dans la session.

  • Contention et latence des verrous de session. Le verrouillage de session introduit une surcharge de latence, car chaque consommateur doit acquérir un verrou exclusif sur une session avant de traiter les messages. Lorsqu’un consommateur contient un verrou de session, aucun autre consommateur ne peut traiter les messages de cette session, même si le consommateur est lent ou temporairement bloqué. Si la durée du verrouillage est trop courte, l’expiration du verrou peut entraîner le retraitement des messages. Si la durée du verrou est trop longue, un consommateur bloqué retarde la récupération. Ajustez la durée du verrouillage de session en fonction du temps de traitement des messages attendu et implémentez le renouvellement des verrous pour les opérations plus longues.

  • Montée en charge et coût côté client. Le parallélisme d’une session à l’autre se traduit par des instances de consommateurs concurrentes. Dans un modèle serverless tel que Azure Functions, chaque session active est mappée à une exécution simultanée et, dans un modèle dédié, elle est mappée à une instance ou à un thread. Le nombre de sessions actives influence donc directement le coût de calcul. Planifiez les limites de montée en charge des consommateurs et les mécanismes de contrôle de la simultanéité afin d’équilibrer le débit et les coûts.

Quand utiliser ce modèle

Utilisez ce modèle dans les situations suivantes :

  • Les messages arrivent dans l’ordre et doivent être traités dans le même ordre.
  • Les messages peuvent être classés afin que chaque catégorie devienne une unité d’échelle indépendante pour le système.

Ce modèle peut ne pas convenir lorsque :

  • Vous vous attendez à des scénarios à débit extrêmement élevé (millions de messages par minute), car l’exigence FIFO limite la mise à l’échelle que le système peut atteindre.

  • L’ordre des messages n’est pas obligatoire. Lorsque les messages peuvent être traités indépendamment dans n’importe quel ordre, le modèle Consommateurs concurrents offre une mise à l’échelle horizontale plus simple sans la surcharge de coordination du verrouillage de session.

Conception de la charge de travail

Évaluez comment utiliser le convoi séquentiel dans la conception d'une charge de travail pour répondre aux objectifs et principes abordés dans les piliers du framework Azure Well-Architected. 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 utilise l’ordonnancement FIFO basé sur les sessions pour éliminer les situations de concurrence, la logique de traitement des messages sujette à la contention et d’autres solutions de contournement destinées à gérer des messages ordonnés de manière incorrecte, susceptibles d’entraîner des dysfonctionnements.

- RE :02 Flux critiques
- RE :07 Travaux en arrière-plan

Si ce modèle introduit des compromis au sein d’un pilier, considérez-les contre les objectifs des autres piliers.

Example

Sur Azure, vous pouvez implémenter ce modèle à l’aide de sessions de messages Service Bus. Pour les consommateurs, vous pouvez utiliser soit Azure Logic Apps avec le connecteur Service Bus peek-lock, soit Azure Functions avec le déclencheur Service Bus.

Lorsqu’un producteur définit la SessionId propriété sur un message, Service Bus regroupe tous les messages qui partagent le même ID de session en une seule session logique. Un client accepte la session et obtient un verrouillage exclusif sur celle-ci. Ce verrou garantit qu’un seul consommateur traite les messages de cette session à tout moment et que les messages arrivent dans l’ordre FIFO. D’autres consommateurs peuvent accepter et traiter simultanément différentes sessions, en fournissant un débit parallèle entre les groupes.

Dans l’exemple de suivi des commandes, le système traite chaque message de registre dans l’ordre dans lequel il est reçu et envoie chaque transaction à une autre file d’attente où la catégorie est définie sur l’ID de commande. Une transaction ne s’étend jamais sur plusieurs commandes dans ce scénario, donc les consommateurs traitent les catégories en parallèle, mais selon le principe FIFO au sein de chaque catégorie.

Le processeur de registre effectue une distribution ramifiée des messages en dégroupant le contenu de chaque message dans la première file d’attente :

Diagramme de l’exemple d’architecture de convoi séquentiel. Il affiche un producteur, une file d’attente de registre, un processeur de registre, une file d’attente de transactions et trois processeurs de commandes.

Le diagramme passe de gauche à droite sur cinq étapes. À l’extrême gauche se trouve une boîte intitulée « producer ». Une flèche part du producteur et pointe vers une boîte centrale étiquetée « ledger queue ». Une flèche va de la file d’attente du registre vers une case intitulée « ledger processor ». Une flèche pointe du processeur du registre vers une boîte libellée « file d’attente des transactions ». Dans la file d’attente des transactions, trois flèches pointent vers la droite, chacune étiquetée avec une catégorie de session. La flèche supérieure est étiquetée « transactions de commande A » et pointe vers une case étiquetée « processeur de commandes A ». La flèche centrale est étiquetée « transactions de commande B » et pointe vers une case étiquetée « processeur de commandes B ». La flèche inférieure est étiquetée « transactions de commande C » et pointe vers une case étiquetée « processeur de commandes C ». Le diagramme illustre la transition de série à parallèle : le producteur envoie tous les messages de manière séquentielle via la file d’attente du registre et le processeur du registre, et le processeur du registre affecte à chaque message l’ID de session correspondant à l’ID de commande avant de l’ajouter à la file d’attente des transactions. La file d’attente des transactions achemine ensuite les messages de chaque commande exclusivement vers le processeur de commandes correspondant, ce qui permet au processeur de commandeS A, au processeur de commandeS B et au processeur de commandeS C de consommer leurs sessions respectives en parallèle et dans l’ordre FIFO.

Le processeur de registre effectue trois étapes :

  1. Parcours du registre, une transaction à la fois.
  2. Définit l’ID de session du message pour qu’il corresponde à l’ID de commande.
  3. Envoi de chaque transaction du registre vers une file d’attente secondaire avec l’ID de session défini sur l’ID de la commande.

Les consommateurs surveillent la file d’attente secondaire et traitent tous les messages dont les identifiants de commande correspondent, dans l’ordre FIFO. Les consommateurs utilisent le mode peek-lock.

La file d’attente du registre est un point de transition série-à-parallèle : toutes les transactions le traversent de manière séquentielle avant de sortir du traitement parallèle basé sur la session. Cette phase de sérialisation constitue le principal goulot d’étranglement en matière de passage à l’échelle, car elle conditionne le débit de l’ensemble de la chaîne de traitement en aval. Toutefois, après que le processeur de registre a distribué des messages à la file d’attente secondaire, les consommateurs peuvent effectuer une mise à l’échelle indépendamment entre les sessions, une par ID de commande.

Technologies de prise en charge

  • sessions de messages Service Bus : regroupe les messages par ID de session et impose un traitement FIFO au sein de chaque session. Les sessions de message sont le mécanisme de Azure principal permettant d’implémenter le modèle de convoi séquentiel.

  • Déclencheur Service Bus pour Azure Functions : prend en charge les déclencheurs basés sur des sessions, qui permettent aux instances de fonction de traiter les messages d’une même session à la fois.

  • Connecteur Service Bus pour Logic Apps : fournit un connecteur Service Bus prenant en charge le mode Peek-Lock pour consommer des files d’attente avec sessions activées dans un traitement basé sur un workflow.

Contributeurs

Microsoft conserve cet article. Les contributeurs suivants ont écrit cet article.

Auteur principal :

Pour afficher les profils LinkedIn non publics, connectez-vous à LinkedIn.

  • Modèle des consommateurs concurrents : plusieurs consommateurs extraient des messages en parallèle à partir d’une file d’attente partagée, ce qui augmente le débit, mais supprime les garanties d’ordre des messages. Le modèle de convoi séquentiel répond à l’écart de commande introduit par les consommateurs concurrents. Il résout cet écart en partitionnant les messages en sessions à clé de catégorie et en traitant chaque session de manière séquentielle.

  • Modèle de nivellement de charge basé sur une file d’attente : une file d’attente met le travail en mémoire tampon entre les producteurs et les consommateurs afin d’absorber les pics d’activité et de lisser une charge irrégulière. Le modèle Sequential Convoy s’appuie sur cette mise en tampon en ajoutant un partitionnement basé sur les sessions, de sorte que la file d’attente équilibre la charge entre les catégories tout en préservant l’ordre FIFO au sein de chaque catégorie.

  • Modèle de file d’attente prioritaire : les messages sont routés vers des files d’attente distinctes ou une priorité donnée dans une file d’attente afin que le travail de priorité supérieure soit traité avant le travail de priorité inférieure. Lorsqu’il faut également préserver l’ordre au sein d’un niveau de priorité, le modèle de convoi séquentiel peut être combiné avec une file d’attente par priorité afin de garantir un traitement FIFO dans chaque session associée à une clé de priorité.

  • Message Peek-Lock (lecture non destructive) : cette opération récupère et verrouille de manière atomique un message dans une file d’attente ou un abonnement en vue de son traitement.

  • Remise ordonnée de messages corrélés dans Logic Apps à l’aide des sessions Service Bus : cet article de blog décrit la prise en charge, par Logic Apps, du modèle de convoi séquentiel.