Maîtrise des paramètres de prélecture RabbitMQ pour des performances consommateur optimales
Réglez la prélecture RabbitMQ pour que les consommateurs restent occupés sans accumuler de messages ni cacher un traitement lent.
Maîtrise des paramètres de prélecture RabbitMQ pour des performances consommateur optimales
La prélecture RabbitMQ est l'un de ces paramètres qui semble minuscule et change tout. Il contrôle le nombre de messages non acquittés que RabbitMQ autorise un consommateur à détenir à la fois. Réglez-le trop bas et les consommateurs rapides passent trop de temps à attendre la prochaine livraison. Réglez-le trop haut et les consommateurs lents accumulent silencieusement le travail, augmentent la latence et rendent les graphiques de profondeur de file d'attente trompeurs.
La façon utile de penser à la prélecture est le travail non terminé. Une prélecture de 20 signifie qu'un consommateur peut avoir 20 messages livrés mais pas encore acquittés. Ces messages ne sont plus prêts dans la file d'attente. Ils sont non acquittés, chez le consommateur jusqu'à ce qu'il acquitte, rejette, refuse ou se déconnecte.
Cela signifie que la prélecture n'est pas seulement un bouton de débit. C'est un bouton d'équité, un bouton de mémoire et un bouton de reprise après panne.
Ce que fait basic.qos dans RabbitMQ
Les consommateurs définissent la prélecture avec basic.qos. Dans la plupart des bibliothèques clientes, vous définissez prefetch_count ; prefetch_size est rarement utilisé et est généralement laissé à zéro.
En Python avec Pika :
channel.basic_qos(prefetch_count=10)
channel.basic_consume(
queue="jobs",
on_message_callback=handle_message,
auto_ack=False,
)
En Node.js avec amqplib :
await channel.prefetch(10);
await channel.consume("jobs", async (msg) => {
try {
await handleMessage(msg.content);
channel.ack(msg);
} catch (err) {
channel.nack(msg, false, false);
}
}, { noAck: false });
L'accusé de réception manuel est important. Si vous utilisez des accusés de réception automatiques, RabbitMQ considère le message comme terminé dès qu'il est livré. La prélecture ne protège plus la fiabilité du traitement de la même manière, car il n'y a pas de fenêtre non acquittée à gérer.
RabbitMQ applique la prélecture par consommateur par défaut dans l'utilisation moderne, même si le libellé original d'AMQP est orienté canal. Certains clients exposent un indicateur global. Soyez prudent avec celui-ci. Une limite partagée au niveau du canal ou de la connexion peut créer des interactions déroutantes entre les consommateurs. La plupart des services sont plus faciles à raisonner lorsque chaque consommateur a son propre canal et son propre nombre de prélecture.
Pourquoi la prélecture modifie la latence
Imaginez une file d'attente avec deux consommateurs. Le consommateur A reçoit un lot de 100 messages, puis rencontre une API externe lente. Le consommateur B est sain et rapide, mais ces 100 messages sont déjà attribués à A. RabbitMQ ne les donnera pas à B à moins que A ne les rejette ou que son canal ne se ferme.
Du point de vue de la file d'attente, ces messages ne sont pas prêts. Du point de vue de l'utilisateur, ils sont retardés. C'est pourquoi une prélecture élevée peut donner l'impression qu'un système est meilleur dans les graphiques du courtier tout en aggravant la latence réelle.
Une prélecture faible donne à RabbitMQ plus de chances de répartir équitablement le travail. Une prélecture élevée donne aux consommateurs plus de travail local et moins d'allers-retours avec le courtier. Aucune des deux n'est toujours correcte.
Valeurs de départ qui ont du sens
Pour les tâches lentes, commencez petit. Si chaque message appelle une API tierce, écrit plusieurs lignes de base de données ou effectue des transformations lourdes en CPU, essayez prefetch_count=1 à 10. Vous voulez qu'un consommateur défaillant ou lent ne détienne qu'une petite quantité de travail.
Pour les tâches moyennes qui prennent des dizaines ou des centaines de millisecondes et s'exécutent sur des travailleurs stables, des valeurs comme 10, 20 ou 50 sont des points de départ courants. Mesurez avant d'aller plus haut.
Pour les gestionnaires très rapides où le courtier et le consommateur sont sur un réseau à faible latence, une prélecture plus élevée peut réduire les allers-retours et améliorer le débit. Même dans ce cas, évitez de choisir un nombre énorme simplement parce qu'il a rendu un benchmark beau pendant cinq minutes. Surveillez la mémoire du consommateur et la latence de queue.
Une règle empirique simple consiste à dimensionner la prélecture autour de la quantité de travail qu'un consommateur peut confortablement détenir pendant une courte fenêtre. Si un travailleur traite environ 20 messages par seconde et que vous êtes à l'aise avec environ une seconde de travail local mis en mémoire tampon, une prélecture proche de 20 est une expérience raisonnable.
Comment savoir si la prélecture est trop élevée
La prélecture est probablement trop élevée lorsque :
messages_unacknowledgedest important par rapport aux consommateurs actifs.- Certains consommateurs ont beaucoup de messages non acquittés tandis que d'autres sont inactifs.
- La latence des messages est élevée même lorsque
messages_readyest faible. - La mémoire du consommateur augmente pendant les pics.
- Un crash de consommateur provoque une grande vague de redistributions.
Ce dernier point est facile à manquer. Si un travailleur détient 1 000 messages non acquittés et plante, RabbitMQ peut redistribuer ces messages. C'est un comportement correct, mais cela peut créer une pression de duplication sur les systèmes en aval si le gestionnaire n'est pas idempotent.
Réduire la prélecture améliore souvent l'équité et le comportement de récupération. Cela peut réduire un peu le débit de pointe, mais peut améliorer la latence que les utilisateurs ressentent réellement.
Comment savoir si la prélecture est trop faible
La prélecture est probablement trop faible lorsque :
- Les consommateurs ont une faible utilisation du CPU et de la mémoire tandis que
messages_readycontinue de croître. - Le temps de traitement est très court, mais le taux de livraison est limité.
- La latence réseau entre les consommateurs et RabbitMQ est perceptible.
- L'augmentation de la prélecture améliore le débit sans augmenter la latence de queue ou la pression mémoire.
L'exemple classique est un travailleur rapide qui effectue un petit calcul en mémoire et acquitte immédiatement. Avec prefetch_count=1, il peut passer trop de temps à attendre le message suivant. Augmenter la prélecture lui donne un petit tampon local et le maintient occupé.
Ne cachez pas les goulots d'étranglement en aval
Le réglage de la prélecture ne corrigera pas une base de données lente. Il ne peut que modifier la façon dont le travail est distribué et mis en mémoire tampon. Si chaque message attend la même API surchargée, une prélecture plus élevée peut améliorer brièvement le débit tout en augmentant les délais d'attente et les nouvelles tentatives.
Mesurez à l'intérieur du consommateur. Enregistrez ou émettez des métriques pour le temps passé à décoder le message, à attendre la base de données, à appeler des services externes et à acquitter. RabbitMQ peut vous montrer les compteurs prêts et non acquittés, mais il ne peut pas vous dire pourquoi votre gestionnaire prend huit secondes.
Lorsqu'un service en aval est limité en débit, la prélecture doit souvent être plus faible, pas plus élevée. Laissez la file d'attente absorber le backlog visiblement au lieu de cacher des milliers d'appels en vol à l'intérieur des travailleurs.
La prélecture et la concurrence sont différentes
Une prélecture de 50 ne signifie pas automatiquement que votre consommateur traite 50 messages en parallèle. Cela signifie seulement que RabbitMQ peut livrer 50 messages avant de recevoir des accusés de réception. Qu'ils s'exécutent simultanément dépend de votre code consommateur.
Un consommateur monothread avec une prélecture de 50 peut traiter un message à la fois tandis que 49 attendent en mémoire. Un pool de travailleurs avec une concurrence de 10 et une prélecture de 50 peut garder dix tâches actives et quarante en mémoire tampon. Parfois, ce tampon est utile. Parfois, ce n'est que de la latence.
Faites correspondre la prélecture à la concurrence réelle. Si votre processus peut exécuter cinq gestionnaires à la fois, une prélecture de 5 à 20 est plus facile à raisonner que 500.
Compromis d'ordre et d'équité
Les files d'attente RabbitMQ préservent l'ordre au niveau de la file d'attente, mais le comportement du consommateur peut modifier l'ordre dans lequel le travail se termine. Avec plusieurs consommateurs et une prélecture supérieure à 1, le message 20 peut se terminer avant le message 3 parce qu'il est allé vers un travailleur plus rapide ou avait un travail plus facile.
Pour la plupart des files d'attente de travail, l'ordre d'achèvement n'a pas d'importance. Pour les mises à jour de compte, les changements d'inventaire ou les flux de travail qui doivent être traités en séquence, cela peut être très important. Dans ces cas, utiliser une file d'attente par clé d'ordre, partitionner par clé ou garder la prélecture faible peut être plus sûr que de rechercher un débit maximal.
L'équité a un compromis similaire. Une prélecture faible permet à RabbitMQ de distribuer le travail plus uniformément car les consommateurs reviennent chercher des messages plus souvent. Une prélecture élevée récompense les consommateurs qui reçoivent les messages en premier. Si les messages ont des temps de traitement inégaux, cela peut conduire à ce qu'un travailleur détienne un tas de tâches lentes tandis qu'un autre termine rapidement son lot.
Quand les gens disent "l'équilibrage de charge RabbitMQ est inégal", la prélecture est l'une des premières choses à vérifier. Le courtier ne peut équilibrer que les messages qui n'ont pas déjà été livrés.
Le comportement en cas d'échec est important
La prélecture modifie ce qui se passe lorsqu'un consommateur meurt. Avec prefetch_count=1, une livraison non acquittée revient lorsque le canal se ferme. Avec prefetch_count=500, des centaines peuvent revenir à la fois. Si le consommateur a effectué des effets secondaires partiels avant de planter, ces redistributions peuvent déclencher des écritures en double, des e-mails en double ou des appels API en double, sauf si le gestionnaire est idempotent.
Cela ne signifie pas qu'une prélecture élevée est mauvaise. Cela signifie qu'une prélecture élevée appartient aux gestionnaires idempotents, avec des règles de nouvelle tentative claires et une surveillance des taux de redistribution. Si le traitement en double serait dangereux, gardez la fenêtre non acquittée petite jusqu'à ce que l'application soit construite pour la gérer.
Regardez l'indicateur redelivered dans le consommateur. Ce n'est pas un compteur de nouvelles tentatives complet, mais c'est un signal utile que le message a déjà été livré. Pour des limites de nouvelles tentatives robustes, suivez les tentatives dans les en-têtes ou dans l'état de l'application et acheminez les messages épuisés vers une file d'attente de lettres mortes.
Plusieurs files d'attente et charges de travail mixtes
Une valeur de prélecture convient rarement à toutes les files d'attente. Un service qui consomme thumbnail.generate et email.send peut avoir besoin de paramètres différents pour chacun. La génération de miniatures peut être lourde en CPU et mieux adaptée à une faible concurrence. L'envoi d'e-mails peut être lié au réseau et tolérer plus de messages en vol.
Si un seul processus consomme plusieurs files d'attente sur un seul canal, le comportement QoS peut devenir plus difficile à raisonner. Préférez des canaux séparés pour des charges de travail significativement différentes. Cela rend la prélecture, la surveillance et la gestion des échecs plus évidentes.
Les tailles de messages mixtes sont un autre signe d'avertissement. Si une file d'attente contient à la fois de petits événements et d'énormes charges utiles, une prélecture basée sur le nombre ne reflète pas bien la pression mémoire. Dix petits messages et dix gros messages n'ont pas le même coût. Dans cette situation, divisez la charge de travail ou déplacez les charges utiles volumineuses hors de RabbitMQ et passez des références à la place.
Surveillez les non acquittés par consommateur, pas seulement par file d'attente
Un compteur de non acquittés au niveau de la file d'attente vous indique qu'il y a du travail non terminé, mais il peut cacher un déséquilibre. Un consommateur peut détenir la plupart des messages non acquittés tandis que les autres sont presque vides. Cela indique souvent une prélecture élevée, un coût de message inégal ou un travailleur malsain.
Utilisez les métriques au niveau du consommateur depuis l'interface de gestion, Prometheus ou rabbitmqctl list_consumers lors d'un test. Si la distribution est inégale, réduire la prélecture ou diviser les types de messages lents peut améliorer la latence réelle même si le débit total ne change que peu.
Revisitez la prélecture après les déploiements
Les valeurs de prélecture vieillissent. Une valeur qui fonctionnait lorsqu'un gestionnaire n'écrivait qu'une seule ligne de base de données peut être erronée après que la prochaine version ajoute un appel API, une validation supplémentaire ou une charge utile plus volumineuse. Traitez la prélecture comme faisant partie de la configuration des performances, pas un nombre que vous définissez une fois et oubliez.
Après une version consommateur, comparez la latence de traitement, les compteurs non acquittés, les redistributions et la mémoire du consommateur avec la version précédente. Si la latence augmente mais que le CPU n'est pas saturé, le gestionnaire peut attendre quelque chose d'externe et une prélecture plus faible peut maintenir le système plus équitable. Si le CPU est élevé et que chaque message est lié au CPU, ajouter des travailleurs ou réduire le travail par message peut être plus important que de modifier la prélecture.
Documentez la raison de la valeur choisie près de la configuration du consommateur. Les mainteneurs futurs doivent savoir si prefetch_count=5 a été choisi pour l'équité, la mémoire, l'ordre, les limites de débit en aval, ou simplement comme valeur par défaut temporaire.
Testez avec des formes de messages réelles
Ne réglez pas la prélecture avec de minuscules messages factices si les messages de production sont de grandes charges utiles JSON ou incluent des recherches coûteuses en base de données. La taille du message et le coût du gestionnaire sont importants.
Une boucle de test utile est :
- Choisissez une valeur de prélecture.
- Exécutez un taux de publication réaliste suffisamment longtemps pour voir un comportement stable.
- Surveillez
messages_ready,messages_unacknowledged, CPU du consommateur, mémoire du consommateur, latence de traitement et taux d'erreur. - Tuez un consommateur et voyez combien de messages sont redistribués.
- Augmentez ou diminuez la prélecture et répétez.
La meilleure valeur est rarement celle avec le débit de référence court le plus élevé. C'est la valeur qui maintient les consommateurs occupés, maintient la latence acceptable et échoue d'une manière que votre système peut gérer.
Une valeur par défaut pratique
Si vous n'avez pas encore de données, commencez avec des accusés de réception manuels et prefetch_count=10 pour les files d'attente de travail ordinaires. Utilisez 1 pour un traitement lent, coûteux ou strictement équitable. Essayez 20 ou 50 pour les gestionnaires rapides et stables après avoir mesuré. Allez plus haut uniquement lorsque les métriques montrent que les allers-retours de livraison sont le goulot d'étranglement et que les consommateurs ont une marge mémoire.
Le réglage de la prélecture RabbitMQ n'est pas une configuration unique. Revisitez-le lorsque la taille du message change, le code consommateur change, les dépendances en aval changent ou vous ajoutez plus d'instances de travailleurs. La bonne valeur de prélecture est celle qui correspond à la forme actuelle du travail.