Dépannage du traitement lent des messages : identifier les goulots d'étranglement RabbitMQ
Diagnostiquez les ralentissements RabbitMQ en séparant les goulots d'étranglement liés au producteur, au courtier, à la file d'attente, au consommateur, au disque et aux confirmations.
Dépannage du traitement lent des messages : identifier les goulots d'étranglement RabbitMQ
Lorsqu'une file d'attente RabbitMQ s'accumule, la file d'attente ne fait que montrer le symptôme. Le goulot d'étranglement peut être un consommateur lent, un éditeur bloqué, une alarme disque, une mauvaise valeur de prélecture, une charge utile de message énorme ou une base de données en aval qui a discrètement commencé à expirer. Redémarrer RabbitMQ peut effacer le graphique pendant quelques minutes, mais cela résout rarement la raison pour laquelle les messages étaient lents.
Le chemin de dépannage le plus rapide consiste à séparer le flux en morceaux : publication dans RabbitMQ, routage vers les files d'attente, stockage des messages, livraison aux consommateurs, traitement du travail et accusé de réception de l'achèvement. Chaque morceau laisse des preuves différentes.
Première division : prêt ou non accusé réception
Commencez par les compteurs de file d'attente :
rabbitmqctl list_queues name messages_ready messages_unacknowledged consumers state
messages_ready signifie que les messages sont dans la file d'attente en attente d'être livrés. Si ce nombre augmente, RabbitMQ n'a pas de consommateurs disponibles, les consommateurs sont à leur limite de prélecture ou la livraison est bloquée par une autre condition.
messages_unacknowledged signifie que les messages ont déjà été livrés aux consommateurs et que RabbitMQ attend un ack, nack, rejet ou fermeture de canal. Si ce nombre augmente, le goulot d'étranglement se trouve généralement à l'intérieur du consommateur ou de quelque chose que le consommateur appelle.
Cette distinction est importante. Si les messages prêts sont élevés et les non accusés réception faibles, ajouter plus de mémoire au courtier ne fera pas apparaître les consommateurs. Si les non accusés réception sont élevés, ajouter plus de partitions de file d'attente peut ne pas aider car le travail a déjà quitté la file d'attente.
Vérifiez si les consommateurs sont réellement présents
Un nombre surprenant d'incidents "RabbitMQ est lent" sont en réalité des incidents "les consommateurs ne fonctionnent pas". Le déploiement a échoué, la mise à l'échelle automatique est passée à zéro, les informations d'identification ont changé, le mauvais hôte virtuel a été utilisé ou le service est connecté à l'environnement de staging tandis que les producteurs publient en production.
Utilisez :
rabbitmqctl list_consumers queue_name channel_pid consumer_tag ack_required prefetch_count active
S'il n'y a pas de consommateurs, corrigez cela d'abord. Si des consommateurs sont présents mais inactifs, vérifiez les journaux d'application et l'état de la connexion. Si chaque consommateur a une prélecture de 1 et que chaque message prend plusieurs secondes, une faible concurrence de livraison peut être attendue. Si chaque consommateur a une prélecture de 500 et que les non accusés réception sont énormes, les consommateurs peuvent accumuler du travail qu'ils ne peuvent pas terminer rapidement.
Mesurez le temps de traitement du consommateur
RabbitMQ peut vous dire que les messages ne sont pas accusés réception. Il ne peut pas vous dire si le consommateur analyse une charge utile géante, attend PostgreSQL, réessaie un appel HTTP ou est bloqué sur un verrou.
Ajoutez une temporisation autour du gestionnaire réel :
message_received_at
decode_ms
business_logic_ms
database_ms
external_api_ms
ack_ms
message_completed_at
Vous n'avez pas besoin d'un système de traçage parfait pour apprendre quelque chose. Même quelques champs de journal structurés peuvent montrer que le gestionnaire prend normalement 80 ms mais passe maintenant 4 secondes à attendre une API en aval.
Si le travail est lent mais parallélisable, ajoutez des instances de consommateur ou augmentez la concurrence interne des travailleurs. Si le système en aval est la limite, ajouter des consommateurs peut aggraver les choses. Vous pouvez avoir besoin de limitation de débit, de traitement par lots, de mise en cache ou d'une file d'attente de nouvelle tentative séparée.
Ajustez la prélecture après avoir compris le gestionnaire
La prélecture contrôle le nombre de messages non accusés réception que RabbitMQ peut envoyer à chaque consommateur. Elle est souvent impliquée dans les incidents de traitement lent car elle modifie l'endroit où le backlog est visible.
Avec une faible prélecture, les messages restent prêts dans RabbitMQ jusqu'à ce qu'un consommateur soit prêt pour plus. C'est équitable et facile à observer, mais cela peut sous-utiliser des consommateurs très rapides.
Avec une prélecture élevée, les messages se déplacent rapidement vers les consommateurs. Cela peut améliorer le débit pour les gestionnaires rapides, mais cela peut également masquer la latence. Un consommateur lent avec une grande valeur de prélecture peut rester assis sur des centaines de messages pendant que d'autres consommateurs manquent de travail.
Une mesure pratique en cas d'incident est de réduire la prélecture pour les consommateurs lents ou instables et d'observer si la latence de queue s'améliore. Pour les consommateurs rapides avec une faible utilisation du processeur et des comptes de prêts élevés, augmentez prudemment la prélecture et mesurez à nouveau.
Recherchez les goulots d'étranglement côté éditeur
Parfois, la file d'attente ne s'accumule pas parce que les consommateurs sont lents. Elle s'accumule parce que les producteurs publient par rafales et attendent ensuite inefficacement les confirmations.
Les confirmations d'éditeur sont le bon outil lorsque les éditeurs ont besoin de savoir que RabbitMQ a accepté les messages. Le modèle lent consiste à attendre chaque confirmation avant de publier le message suivant. Cela transforme chaque publication en un aller-retour.
De meilleurs modèles utilisent des confirmations asynchrones ou des lots limités. L'éditeur peut garder un nombre limité de messages en vol, gérer les nacks et éviter de bloquer sur chaque message individuel. La limite est importante. La publication illimitée en vol peut déplacer le goulot d'étranglement vers la mémoire de l'éditeur ou la pression du courtier.
Vérifiez les métriques de l'éditeur : taux de publication, latence de confirmation, nombre de confirmations en vol, reconnexions, messages retournés et exceptions de canal. Dans l'interface de gestion, comparez les taux de publication avec les taux de livraison/accusé de réception. Si la publication est faible même si l'application est occupée, le producteur peut attendre des confirmations, des transactions ou des changements de connexion.
Évitez les transactions AMQP pour la publication à haut débit sauf s'il y a une raison spécifique. Elles sont beaucoup plus coûteuses que les confirmations d'éditeur pour une publication fiable typique.
Surveillez le disque avant de blâmer RabbitMQ
Les messages persistants, les files d'attente de quorum, les flux et les grands backlogs impliquent tous le disque. Lorsque la latence du disque augmente, le flux de messages peut ralentir considérablement.
Sur le nœud RabbitMQ, vérifiez :
rabbitmq-diagnostics status
rabbitmq-diagnostics alarms
rabbitmq-diagnostics memory_breakdown
Au niveau du système d'exploitation, utilisez des outils tels que iostat, vmstat ou vos graphiques de surveillance cloud. Regardez la latence du disque et l'attente d'E/S, pas seulement le débit. Un disque cloud qui a épuisé ses crédits de rafale peut sembler normal en configuration et terrible en pratique.
Si le disque est le goulot d'étranglement, les correctifs possibles incluent un stockage plus rapide, moins d'écritures persistantes, des messages plus petits, un meilleur regroupement des confirmations d'éditeur, le fractionnement de la file d'attente ou le déplacement des charges de travail de type relecture vers des flux ou un autre système orienté journal. Ne désactivez pas la persistance pour les messages que l'entreprise ne peut pas perdre juste pour rendre un graphique vert.
Vérifiez les alarmes et les connexions bloquées
RabbitMQ se protège avec des alarmes de mémoire et de disque. Lorsqu'une alarme est active, les éditeurs peuvent être bloqués. Cela peut ressembler à une lenteur d'application du côté du producteur.
Exécutez :
rabbitmq-diagnostics alarms
rabbitmqctl list_connections name user state channels send_pend recv_cnt send_cnt
Si des alarmes de mémoire sont actives, trouvez si la mémoire est occupée par des files d'attente, des connexions, des messages non accusés réception, des binaires ou des plugins. Si des alarmes de disque sont actives, libérez de l'espace ou ajoutez de la capacité avant d'essayer de pousser plus de messages à travers le courtier.
Les connexions bloquées ne sont pas un bug en soi. C'est RabbitMQ qui dit aux éditeurs de ralentir parce que le nœud protège la disponibilité.
La taille du message peut être le coupable silencieux
Un système qui gère 10 000 petits messages par seconde peut avoir du mal avec 500 gros messages par seconde. Les charges utiles volumineuses augmentent le transfert réseau, la pression mémoire, les écritures disque, le travail de récupération de place et le temps de traitement du consommateur.
Si les messages contiennent de gros documents, images, rapports ou grands tableaux, envisagez de stocker la charge utile dans un stockage d'objets ou une base de données et d'envoyer une référence via RabbitMQ. Incluez suffisamment de métadonnées pour le routage et l'idempotence, mais gardez le courtier hors du rôle de stockage en vrac lorsque cela est possible.
Vérifiez également les choix de compression. La compression de charges utiles énormes peut réduire l'utilisation du réseau et du disque mais augmenter le processeur. Savoir si cela aide dépend de l'endroit où se trouve le goulot d'étranglement.
Les nouvelles tentatives peuvent créer le goulot d'étranglement
Un service en aval défaillant peut transformer un message en plusieurs tentatives. Si les consommateurs remettent immédiatement en file d'attente les échecs, ils peuvent traiter les mêmes mauvais messages à plusieurs reprises pendant que le nouveau travail attend. La profondeur de la file d'attente peut augmenter, le processeur peut sembler occupé et très peu de travail utile est effectué.
Recherchez des taux de redistribution élevés et des journaux d'erreurs répétés avec les mêmes ID de message. Si la même charge utile échoue encore et encore, déplacez-la hors du flux principal. Un échange de lettres mortes, une file d'attente de nouvelle tentative différée ou un mécanisme de nouvelle tentative planifiée donne à la dépendance le temps de récupérer et empêche les messages empoisonnés de bloquer le travail normal.
Soyez prudent avec les tempêtes de nouvelles tentatives. Si une API est en panne pendant dix minutes et que chaque message réessaie chaque seconde, la récupération devient plus difficile lorsque l'API revient. Utilisez un backoff. Limitez les tentatives. Rendez l'échec final visible dans une file d'attente de lettres mortes avec suffisamment de contexte pour enquêter.
L'idempotence fait également partie du dépannage des performances. Si un consommateur réessaie après avoir partiellement terminé le travail, les doublons peuvent créer une contention de base de données, des erreurs de clé unique ou des appels en aval supplémentaires. Un gestionnaire qui peut traiter en toute sécurité le même message deux fois est beaucoup plus facile à mettre à l'échelle et à récupérer.
Les taux de l'interface de gestion ont besoin de contexte
L'interface de gestion RabbitMQ est utile, mais les graphiques de taux peuvent induire en erreur si vous lisez une seule ligne. Un taux de livraison élevé avec un faible taux d'accusé de réception signifie que le travail est distribué plus rapidement qu'il n'est terminé. Un taux d'accusé de réception élevé avec un nombre de prêts élevé peut signifier que les consommateurs travaillent mais ne suffisent pas à rattraper. Un faible taux de publication pendant un incident peut signifier que les producteurs sont bloqués ou attendent des confirmations.
Regardez plusieurs taux ensemble :
publish: messages entrant dans les échanges.deliver/get: messages envoyés aux consommateurs.ack: messages terminés par les consommateurs.redeliver: messages livrés à nouveau après un échec antérieur ou une fermeture de canal.
Pour une file d'attente de travail stable et saine, les taux de publication et d'accusé de réception doivent être proches dans le temps. Les courtes rafales sont normales. Les longs écarts signifient que le backlog s'accumule ou se vide. Si les redistributions augmentent fortement, n'ajoutez pas simplement plus de consommateurs. Découvrez pourquoi les messages reviennent.
Les fenêtres d'échantillonnage comptent. Un graphique d'une minute peut cacher un blocage de cinq secondes qui nuit aux utilisateurs. Un graphique d'une seconde peut faire paraître les rafales normales comme du chaos. Faites correspondre la fenêtre du graphique à la latence que vos utilisateurs ou systèmes en aval jugent importante.
Séparez le backlog normal du backlog cassé
Tous les backlogs ne sont pas une urgence. Un système par lots peut intentionnellement mettre en file d'attente du travail pendant la journée et le vider la nuit. Un flux de travail orienté utilisateur peut être malsain si les messages attendent trente secondes. La même profondeur de file d'attente peut être acceptable dans un système et grave dans un autre.
Définissez un signal basé sur l'âge, pas seulement un nombre. Le nombre de messages vous indique combien attendent ; l'âge du message vous indique si l'entreprise prend du retard. Si votre surveillance peut suivre l'âge du message le plus ancien ou le temps de bout en bout de la publication à l'accusé de réception, elle détectera les ralentissements plus tôt que la seule profondeur de file d'attente.
Liez les alertes à cette attente. Alerter sur 10 000 messages peut être bruyant pour une file d'attente d'exportation nocturne et bien trop tard pour une file d'attente de réinitialisation de mot de passe. Alerter sur "le message le plus ancien est plus ancien que l'objectif de service" est généralement plus proche de ce que les utilisateurs jugent important.
Une file d'attente chaude reste une file d'attente chaude
L'ajout de nœuds de cluster ne divise pas automatiquement une file d'attente sur tous les nœuds. Une seule file d'attente chaude peut rester limitée par son leader, ses consommateurs et son chemin de stockage.
Si une file d'attente transporte des types de travail non liés, divisez-la par comportement de traitement réel. Par exemple, le redimensionnement d'images, l'envoi d'e-mails et la capture de facturation ne devraient pas partager une file d'attente générique jobs s'ils ont des besoins de latence et de nouvelle tentative différents. Des files d'attente séparées vous permettent de mettre à l'échelle les consommateurs indépendamment et d'isoler les messages empoisonnés.
Si un type de travail est toujours trop chaud, partitionnez uniquement lorsque les exigences de commande le permettent. Le partitionnement par ID client, locataire, région ou une autre clé stable peut fonctionner, mais cela pousse la complexité dans le routage et les opérations. Ne partitionnez pas simplement pour éviter de corriger un gestionnaire lent.
Un ordre de dépannage calme
Lors d'un incident, j'utilise cet ordre :
- Vérifiez les alarmes : mémoire, disque et connexions bloquées.
- Vérifiez les compteurs de file d'attente : prêts, non accusés réception, consommateurs.
- Vérifiez les journaux des consommateurs et la temporisation du gestionnaire.
- Vérifiez la prélecture et la distribution des non accusés réception par consommateur.
- Vérifiez la latence de confirmation de l'éditeur et les messages retournés.
- Vérifiez la latence du disque et la pression sur les ressources du nœud.
- Vérifiez la taille du message et les changements récents de charge utile.
- Ce n'est qu'alors que vous modifiez la topologie ou ajoutez des nœuds de courtier.
Cet ordre évite une erreur courante : mettre à l'échelle le courtier alors que le goulot d'étranglement est un travailleur, ou mettre à l'échelle les travailleurs alors que le goulot d'étranglement est le disque.
RabbitMQ est généralement très clair une fois que vous lisez les bons compteurs. Un nombre de prêts en croissance indique que le travail attend. Un nombre de non accusés réception en croissance indique que le travail est en cours mais ne se termine pas. Un éditeur bloqué indique que le courtier se protège. Traitez chaque signal comme un indice, et la correction devient beaucoup moins dramatique.