Guide d'architecture logicielle
Lissage de charge par file d'attente : évoluer de façon fiable sans surcharger l'origine
Les files absorbent une demande irrégulière uniquement lorsque l'acceptation, l'âge du backlog, la capacité des workers, les nouvelles tentatives et la réalisation métier sont conçus comme un seul système contrôlé.
Sur cette page
Le trafic arrive par pics ; les systèmes en aval ne le peuvent souvent pas. Un lancement produit, une importation batch, une vague de nouvelles tentatives de webhooks ou une dépendance lente peut transformer une chaîne synchrone en une file croissante de connexions en attente. Le lissage de charge par file d'attente sépare le débit d'arrivée du débit de traitement afin que les producteurs soient reconnus rapidement et que les workers consomment à un rythme contrôlé.
Cette séparation est utile, mais ne produit pas automatiquement de la fiabilité. Une file non bornée ne fait que déplacer une panne hors de vue. Une file durable et observable, avec une admission bornée, un traitement idempotent et un objectif de réalisation défini, en fait un mécanisme de mise à l'échelle plutôt qu'un problème de stockage.
Une couche d'orchestration edge change l'endroit où le premier pic atterrit, mais pas la discipline qu'exige ce modèle derrière elle. Optimi route et protège le trafic devant l'origine, et la file est en général le point de contrôle suivant qu'atteint un pic de lancement ; traiter l'admission, l'âge du backlog et la capacité des workers comme un seul système gouverné évite que ce relais ne se transforme en panne plus loin en aval — un signal transverse qu'une pratique d'orchestration managée doit surveiller en continu, pas seulement pendant la semaine de lancement.
Une file échange la réalisation immédiate contre une réalisation contrôlée
Une mise en file réussie signifie que le système a accepté la responsabilité du travail, et non que l'action métier a eu lieu. Publiez et surveillez à la fois la latence d'accusé de réception et le temps jusqu'à ce que le résultat visible pour le client soit achevé. Lorsque l'edge et la file vivent sous un même panneau managé comme MYO, ce couplage permet de repérer un chemin d'acceptation en pleine santé qui masque un backlog en train de caler, plutôt qu'une métrique qui semble correcte partout sauf là où le client la ressent.
Scénario fil rouge : la confirmation de commande d'Acme Shop
Reprenons le scénario d'Acme Shop pour ancrer les choix qui suivent. Une fois une commande acceptée, Acme Shop publie une commande order.confirmation.requested ; l'API accuse réception de l'acceptation durable en quelques dizaines de millisecondes, puis des workers envoient l'e-mail de confirmation et mettent à jour la chronologie de la commande. Une livraison en double ne doit jamais envoyer deux e-mails, et une défaillance temporaire du fournisseur d'e-mail ne doit pas laisser le backlog consommer la capacité de la base de données ou du fournisseur.
L'objectif : accepter une rafale de confirmations, traiter chaque effet métier une seule fois malgré une livraison au moins une fois, et maintenir la réalisation dans un objectif d'âge déclaré — ce qui suppose une file durable avec sa file de dead-letter, un producteur authentifié, un magasin d'idempotence durable, des métriques de santé des workers et d'âge de file, et une limite de concurrence en aval documentée. Ce fil rouge revient dans les sections suivantes.
Décidez quel travail peut devenir asynchrone
Mettez en file le travail qui est durable, peut être réessayé indépendamment et n'est pas requis pour rendre la réponse immédiate : traitement de médias, notifications, mises à jour d'index, génération de rapports, synchronisation de partenaires et de nombreux effets secondaires de webhooks sont des candidats courants. Conservez un chemin synchrone lorsque l'appelant a besoin d'une réponse immédiate et faisant autorité, comme une décision d'autorisation ou une réservation d'inventaire qui ne peut pas être différée sans risque.
Pour chaque opération mise en file, définissez le schéma de commande ou d'événement, l'exigence d'ordre, le propriétaire, la rétention, l'âge maximal acceptable, le comportement des doublons, le modèle d'annulation et le processus de dead-letter. Le message doit porter une identité métier immuable et un contexte de corrélation, et non une copie complète de données de requête non contrôlées ou de secrets.
Le modèle de lissage de charge par file d'attente (Queue-Based Load Leveling) de Microsoft décrit bien le compromis central : une file permet un traitement indépendamment évolutif, mais le système doit tolérer une réalisation éventuelle et gérer la file comme un domaine de défaillance potentiel. Deux modèles complémentaires méritent d'être associés délibérément : Competing Consumers, où plusieurs workers puisent dans la même file pour mettre à l'échelle horizontalement, et Priority Queue, où le travail urgent contourne un long backlog. Acme Shop utilise les deux : plusieurs workers de confirmation se disputent la même file, et une file distincte à haute priorité porte les notifications de réinitialisation de mot de passe et d'échec de paiement, sans jamais attendre derrière un backlog marketing.
Concevez un chemin d'acceptation borné
Le chemin du producteur doit valider l'authentification, l'autorisation, le schéma, la taille du corps et les préconditions métier avant de publier du travail durable. Attribuez une clé d'idempotence aux commandes initiées par le client afin qu'une expiration réseau ou une nouvelle tentative ne crée pas de travail en double. Renvoyez un identifiant d'opération stable, un endpoint de statut ou un modèle de callback, et un état honnête tel qu'accepté ou mis en file.
Bornez la file et le producteur. Définissez des limites pour la taille de message, le backlog total, la part par tenant ou par opération, le débit de publication et la durée de vie des messages. Lorsqu'une limite est atteinte, rejetez ou différez le travail de faible priorité avec une réponse documentée plutôt que d'accepter des messages qui ne peuvent pas respecter leur objectif de réalisation. Une réponse 429 ou 503 peut être plus fiable qu'un backlog de plusieurs heures sans visibilité pour le client.
Placez une validation peu coûteuse et des contrôles d'abus à la périphérie ou à la passerelle. Appliquez des limites de débit par route, des plafonds de taille de requête et des règles WAF lorsque nécessaire ; protégez l'origine avec un réseau privé ou un pare-feu par défaut refusant, du trafic edge-à-origine authentifié et des limites de concurrence de l'origine. N'exposez pas directement un endpoint de publication à l'origine simplement parce que la file de workers est durable.
L'enveloppe du message et le contrat du worker
Gardez les métadonnées de routage et de déduplication hors de la charge utile métier : identifiant de message, type d'événement, version de schéma, horodatage d'occurrence, contexte de trace W3C et clé d'idempotence forment une enveloppe minimale au-dessus d'une charge utile qui ne porte que l'identité métier nécessaire, jamais de données client complètes ni de secrets. Le worker revendique le message, crée ou lit un enregistrement durable indexé par cette clé, et traite un enregistrement déjà terminé comme un doublon réussi ; pour un résultat inconnu après une requête externe, il interroge le fournisseur avec sa propre référence avant de renvoyer quoi que ce soit, et n'accuse réception qu'une fois la réalisation durable écrite.
Rendez les consommateurs idempotents et sûrs à réessayer
La plupart des files pratiques fournissent une livraison au moins une fois. Un message peut être livré à nouveau après le crash d'un worker, l'expiration d'un accusé de réception, l'expiration de visibilité ou un basculement. Des effets métier exactement une fois exigent une conception au niveau applicatif, et non de la confiance dans l'étiquette d'un service de file.
Utilisez un enregistrement durable de déduplication ou d'état, indexé par l'opération métier. Rendez l'effet secondaire et son marqueur de traitement atomiques lorsque possible. Lorsqu'un effet secondaire externe ne peut pas participer à la même transaction, utilisez une transactional outbox, une inbox ou un processus de rapprochement et conservez les preuves nécessaires pour résoudre les résultats incertains.
Fixez le délai de traitement en connaissance de cause
La plupart des files managées accordent à un worker une fenêtre bornée pour terminer un message avant qu'il ne redevienne visible pour un autre consommateur — un délai de visibilité chez Amazon SQS, un délai d'accusé de réception chez RabbitMQ. Trop courte, un worker lent mais sain perd le message en plein traitement et un second consommateur le récupère, créant le doublon que la clé d'idempotence doit intercepter, en plus fréquent ; trop longue, un worker réellement en panne laisse le message invisible pendant toute la fenêtre, ce qui élargit silencieusement l'âge du backlog. Fixez le délai à la durée maximale réaliste de traitement, ou démarrez prudemment puis mesurez ; pour un travail à durée variable, prolongez-le depuis le worker lui-même — un heartbeat — plutôt que de deviner une valeur fixe. L'e-mail de confirmation d'Acme Shop utilise ainsi une fenêtre de 30 secondes sans heartbeat, tandis que sa file de rapports, aux tâches de plusieurs minutes, prolonge son délai toutes les 60 secondes tant que le worker est vivant et traite un heartbeat manqué comme une panne.
Classez la défaillance avant de réessayer :
| Classe de défaillance | Traitement habituel |
|---|---|
| Expiration temporaire d'une dépendance | Réessayer avec un backoff exponentiel borné et de la gigue, dans le budget d'âge du message. |
| Service en aval limité en débit | Respecter un délai de nouvelle tentative connu et réduire la concurrence des workers si nécessaire. |
| Schéma invalide ou état métier rejeté de façon permanente | Arrêter les tentatives ; enregistrer une raison sûre et router vers un workflow de revue. |
| Résultat inconnu après une écriture externe | Rapprocher avec une clé d'idempotence ou une consultation faisant autorité avant de répéter. |
Définissez un nombre maximal de livraisons et envoyez les messages épuisés vers une file de dead-letter avec des métadonnées utiles au diagnostic. Une file de dead-letter n'est pas une poubelle : attribuez-en la responsabilité et fournissez une procédure de rejeu sûre, sans rejouer tout un backlog historique dans une dépendance encore défaillante. Donnez-lui une rétention plus longue que la file source — sinon un message qui épuise ses tentatives en fin de fenêtre source disparaît des deux côtés avant d'avoir pu être diagnostiqué — et alertez sur toute arrivée en dead-letter, pas seulement au-delà d'un seuil de volume.
Mettez les workers à l'échelle selon l'âge de la file et la capacité
La profondeur de file seule est incomplète : un million de petits messages et cent messages coûteux n'ont pas la même urgence. Surveillez l'âge du message le plus ancien, le débit d'arrivée, le taux de réalisation réussie, le taux de défaillance, la durée de traitement, les workers actifs et la saturation par dépendance. Ne mettez les workers à l'échelle que tant que la capacité en aval, les pools de bases de données et les quotas tiers peuvent les soutenir.
Estimez le débit nécessaire à partir de la cible de réalisation. Si 10 000 messages doivent se terminer dans les dix minutes suivant un pic et que chaque worker traite durablement 20 messages par seconde, le système doit disposer de suffisamment de capacité saine pour absorber le pic d'arrivée et résorber tout backlog dans cette fenêtre. Ajoutez une marge pour les nouvelles tentatives, déploiements, répartitions de clés inégales et un groupe de workers défaillant.
Limitez la concurrence par dépendance, et pas seulement par worker. Une flotte de workers peut multiplier la pression sur une partition de base de données ou une API partenaire. Utilisez des bulkheads et des contrôles de concurrence adaptatifs afin que les consommateurs de moindre priorité n'affament pas les workflows critiques. Suspendez l'admission ou délestez le travail facultatif avant que la saturation ne devienne une tempête de nouvelles tentatives.
Menez le calcul de capacité jusqu'au bout
Acme Shop anticipe une rafale de lancement de 12 000 messages en 10 minutes, avec un backlog existant de 1 200 messages, pour un objectif d'écouler les deux en 15 minutes. Un worker traite durablement 8 confirmations par seconde, mais seulement 75 % de la capacité nominale reste disponible une fois pris en compte la marge de déploiement, les nouvelles tentatives et le plafond du fournisseur d'e-mail. Le travail à écouler s'élève à 13 200 messages (12 000 + 1 200), pour un débit requis de 13 200 / 900 s ≈ 14,7 messages/seconde ; le débit effectif par worker tombe à 8 × 0,75 = 6 messages/seconde, d'où trois workers (14,7 / 6 arrondi vers le haut), à condition que leur limite combinée en aval atteigne au moins 18 messages/seconde et que les partitions permettent de répartir le travail. C'est une estimation de planification, à confirmer par la mesure réelle, pas une garantie.
Définissez des SLO de backlog qui déclenchent un délestage automatique
Un plan de capacité ne se dégrade en douceur que si quelque chose agit avant l'échec pur et simple. Définissez des SLO de backlog comme des objectifs explicites et surveillés, pas comme des espoirs implicites : un SLO d'âge qui borne jusqu'où le message le plus ancien peut vieillir avant de rompre la promesse faite au client, et un SLO de profondeur qui borne la taille que la file peut atteindre avant de menacer la rétention ou le coût. Mesurez les deux en continu plutôt qu'au moment du déploiement : un problème de backlog est rarement visible côté producteur avant d'avoir déjà coûté plusieurs minutes d'âge.
L'objectif de réalisation d'Acme Shop est de cinq minutes pour les confirmations acceptées. Ses SLO de backlog traduisent cela en deux seuils : avertir au-delà de deux minutes d'âge, et déclencher une astreinte au-delà de quatre — une minute de marge avant de rompre l'objectif client. L'avertissement diffère automatiquement les confirmations marketing au niveau du producteur, sans attendre qu'un humain remarque un graphique ; l'astreinte ajoute des workers dans la limite documentée en aval ou, si elle est saturée, délestage une part plus large de l'admission de faible priorité jusqu'à ce que l'âge récupère — c'est le franchissement du seuil qui change le comportement du producteur, pas la seule profondeur de file.
Codifiez la décision de délestage plutôt que de la laisser au jugement sous pression : quelles catégories sont différées en premier, quelle réponse reçoit l'appelant, combien de temps dure le report, et qui est alerté si le délestage échoue à faire redescendre l'âge.
Préservez l'ordre uniquement là où il compte
L'ordre global est coûteux et souvent inutile. Partitionnez le travail par une clé d'entité, telle qu'un ID de commande ou de compte, lorsque les opérations sur cette entité doivent être traitées en séquence. Attendez-vous à des clés actives et préparez un plan : sérialisation, mise à l'échelle des partitions ou règle métier qui modifie le workflow.
Le mécanisme diffère selon la technologie : une clé de partition Kafka garde les enregistrements d'une entité sur une seule partition ; une file FIFO Amazon SQS ne garantit un ordre strict qu'au sein d'un même group ID ; Azure Service Bus offre des sessions. Aucun ne survit à un consommateur qui traite sa partition ou son groupe avec plus d'un worker à la fois — vérifiez cette contrainte explicitement. Pour une séquence stricte d'étapes sur une même entité, regardez le modèle Sequential Convoy plutôt que de le reconstruire par convention.
Les événements peuvent arriver tard, en double ou dans le désordre. Incluez une version d'événement, un horodatage source et une version d'entité lorsque cela est utile. Les consommateurs doivent vérifier si un message représente encore une action à entreprendre et interroger la source faisant autorité lorsqu'un événement obsolète peut causer un préjudice.
Tracez le travail au-delà de la frontière asynchrone
Propagez le contexte de trace W3C et les ID de corrélation applicative lors de la production et de la consommation de messages. Les conventions de messagerie OpenTelemetry fournissent un vocabulaire neutre vis-à-vis des fournisseurs pour les spans et attributs. Enregistrez l'heure de mise en file, de retrait de file, la tentative de livraison, l'âge de la file, la classe de message, la version du worker, le résultat d'idempotence et le résultat de dépendance sans placer de charges utiles sensibles dans la télémétrie.
Utilisez des objectifs de niveau de service distincts pour l'acceptation et la réalisation. Par exemple, une API peut accuser réception de 99 % des requêtes valides sous 300 ms tandis que 95 % des tâches acceptées se terminent sous cinq minutes. Le premier SLO détecte les problèmes d'admission ; le second détecte les problèmes de worker, dépendance ou capacité. Les deux sont nécessaires pour éviter un système rapide mais bloqué.
Planifiez les pannes et les rejeux
Testez les cas inconfortables : une panne de file, un producteur qui publie deux fois, un worker qui meurt après une écriture externe, un message empoisonné, une dépendance retardée, une défaillance régionale et un rejeu de messages créés sous un ancien schéma. Vérifiez que les alertes détectent l'augmentation de l'âge avant que la rétention ne soit menacée, que les opérateurs peuvent suspendre les consommateurs en sécurité et que la récupération ne submerge pas l'origine.
Pour les conceptions interrégionales, décidez si les files sont isolées par région, répliquées ou routées vers une région active. Indiquez les compromis de point de récupération et de temps de récupération, les contraintes de résidence des données et le comportement de livraison en double lors du basculement. Une stratégie de file multi-fournisseurs peut réduire le risque de concentration, mais les formats de pont, la surveillance, l'authentification et la sémantique de rejeu doivent avoir un responsable explicite.
Validez, récupérez et diagnostiquez
Validez avec une commande de test ou un canari approuvé. La validation positive publie un message, observe un seul enregistrement d'idempotence terminé et confirme la chronologie visible par le client. La validation négative publie la même enveloppe deux fois et confirme que la seconde livraison est accusée de réception sans nouvel appel au fournisseur. La validation de défaillance arrête un worker après que le fournisseur a accepté la requête mais avant l'accusé de réception : le redémarrage doit rapprocher la requête via la clé durable, puis n'enregistrer qu'une seule réalisation. Surveillez l'âge de la file, le nombre de livraisons et les erreurs fournisseur pendant la validation, sans jamais transformer une campagne client réelle en test de charge.
Pour la récupération, réduisez d'abord les producteurs via leur politique d'admission documentée si l'âge augmente plus vite que la capacité ne peut l'absorber. Conservez intactes la file et les preuves de dead-letter, restaurez la dépendance défaillante, puis reprenez les consommateurs par petits incréments. Ne rejouez qu'une cohorte de dead-letter bornée et comprise, vers un chemin isolé ou limité en débit, après avoir corrigé sa cause — jamais un backlog entier à pleine vitesse, et jamais un accusé de réception dont le résultat métier reste inconnu.
Trois symptômes reviennent le plus souvent : un âge qui grimpe signale un débit d'arrivée supérieur au débit de réalisation, à traiter en réduisant l'admission facultative avant d'ajouter des workers ; un même message traité deux fois sans panne évidente trahit une durée de traitement proche du délai de visibilité, à corriger en l'allongeant ou en ajoutant un heartbeat ; un volume de dead-letter qui croît indique un même rejet qui épuise systématiquement les tentatives, à traiter en quarantaine avant de rejouer un petit échantillon.
Liste de contrôle du lissage de charge par file d'attente
Avant la production, confirmez que le travail peut être différé sans risque ; que l'acceptation est durable et idempotente ; que le backlog, l'âge et les parts de tenant sont bornés par des SLO de backlog explicites ; que les consommateurs sont idempotents et que le délai de traitement est dimensionné en connaissance de cause ; que les nouvelles tentatives et le traitement dead-letter ont des responsables et une rétention suffisante pour diagnostiquer ; que la mise à l'échelle des workers respecte les limites en aval ; que les contrôles edge et d'origine protègent les endpoints de publication ; et que les traces relient la requête à la réalisation finale.
Références faisant autorité
- Microsoft Azure Architecture Center: Queue-Based Load Leveling pattern
- Microsoft Azure Architecture Center: Sequential Convoy pattern
- Google SRE Book: Handling Overload
- Google SRE Book: Addressing Cascading Failures
- Amazon SQS : délai de visibilité (Visibility timeout)
- Conventions sémantiques de messagerie OpenTelemetry
- OWASP API Security Top 10
Transformez vos SLO de backlog en discipline managée
Optimi associe le contrôle d'admission au niveau edge à la visibilité MYO sur l'âge de la file, la saturation des workers et la santé des fournisseurs — pour que Performance, Sécurité et Visibilité restent alignées partout où le lissage de charge par file d'attente rencontre votre origine.
Discuter de l'architecture de mise à l'échelle