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é.
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 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é.
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 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.
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.
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.
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. Alertez sur celle-ci, attribuez-en la responsabilité, fournissez une procédure de rejeu sûre et évitez de rejouer tout un backlog historique dans une dépendance qui reste défaillante.
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.
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.
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.
Liste de contrôle du lissage de charge par file
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 ; que les consommateurs sont idempotents ; que les nouvelles tentatives et le traitement dead-letter ont des responsables ; 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
- Google SRE Workbook: Handling Overload
- Google SRE Book: Addressing Cascading Failures
- Conventions sémantiques de messagerie OpenTelemetry
- OWASP API Security Top 10
Mettez à l'échelle les charges par pics sans masquer le backlog
Échangez avec les experts Optimi sur les files, les contrôles d'admission edge, la protection de l'origine et la télémétrie opérationnelle.
Discuter de l'architecture de mise à l'échelle