RabbitMQ est une dépendance d'un logiciel que nous utilisons, donc, forcément, j'ai dû regarder koi ke s'est. Ce shaarli a plus vocation à conserver ce que j'ai compris de RabbitMQ que de t'apprendre des trucs.
RabbitMQ est un courtier en messages (message broker). Il permet à des processus et à des logiciels distincts de s'échanger des messages à travers un réseau (oui, ça fonctionne aussi en local, mais bon…). Il permet de temporiser une communication (en attendant d'avoir de quoi la traiter) donc d'absorber des pics d'utilisation grâce une file d'attente, d'avoir plusieurs écrivains et lecteurs dans une même file d'attente, etc.
J'en retiens deux usages :
commande1 | commande2 && commande3
, en gros.Dans le passé, j'ai utilisé Apache Kafka (voir mes notes). Je retiens, que, comme d'hab', on peut contorsionner un logiciel afin qu'il fasse le même boulot qu'un autre, mais, en gros : Kafka = file d'attente durable / sécurisée + montée en charge facile par ajout de nœuds + stockage de flux (genre un NetFlow) plus que stockage de messages. RabbitMQ = protocole ouvert et normalisé AMQP dont RabbitMQ n'est qu'une implémentation (Kafka = protocole maison) + simplicité d'utilisation (pour les cas basiques).
J'ai un écrivain / producteur dans une file d'attente, et deux lecteurs / consommateurs de cette file d'attente. RabbitMQ attribue les tâches / messages en round-robin : une tâche pour le premier consommateur, une tâche pour le deuxième, une tâche pour le premier, etc. Cet algorithme ne tient pas compte de la charge effective de chaque consommateur : à certains moments, un consommateur avait X tâches en attente alors que l'autre consommateur faisait rien. J'ai rien trouvé permettant de changer l'algorithme de répartition (dispatching) de RabbitMQ.
Pour compenser, on peut configurer le prefect, c'est-à-dire le nombre max de messages non-acquittés que RabbitMQ envoie à un consommateur (ou à un ensemble de consommateurs). Cela se configure sur le consommateur. Ainsi, tant qu'un consommateur n'aura pas acquitté ses messages afin de retomber sous ce seuil, RabbitMQ distribuera la tâche à un autre consommateur. Si aucun consommateur est disponible, le nombre de messages « ready » (messages pas encore distribués) grossira. La documentation nomme ça « fair dispatch ».
Nos consommateurs sont des nœuds celery, un gestionnaire de tâches distribuées Python. Celery lit la file d'attente RabbitMQ et déclenche la fonction Python KiVaBien dans notre code Django. Pour régler le prefect, le paramètre à utiliser est « worker_prefetch_multiplier » donc la variable « CELERYD_PREFETCH_MULTIPLIER » dans /etc/default/celeryd
si l'on utilise le script d'init celery. Par défaut, la valeur est 4. Elle se multiplie par le nombre de threads d'un worker. Comme nous utilisons « worker_concurrency = 1 », le nombre maximal de messages non-acquittés par worker sera 4. Nous avons 2 machine d'encodage, donc 2 celery donc 2 workers, donc un maximum de 8 messages non-acquittés.
Attention : par défaut, celery acquitte le message / la tâche avant d'avoir effectué le boulot. Les tâches réservées (« prefetch ») viennent donc en sus des tâches acquittées qui sont en cours d'exécution (source 1, source 2). Dans mon cas, avec 1 worker, « worker_concurrency = 1 » et « worker_prefetch_multiplier = 4 », 5 tâches seront "en cours" : 1 en cours d'exécution et 4 en attente de traitement et d'acquittement.
Mais, tiens, comment supervise-t-on le nombre de messages dans une file d'attente RabbitMQ ? Cela donne une idée de la charge sur notre service et permet d'anticiper le nombre de consommateurs qu'il convient de lancer afin d'offrir un service acceptable.
Simple : sudo rabbitmqctl -p <virtual_host> list_queues name messages_ready messages_unacknowledged messages
.
On formate le résultat de sortie en récupérant le nom des files d'attente, le nombre de messages prêts, le nombre de messages non-acquittés et le nombre total de messages (ready+unack). Pour le reste : voir le manuel.
Les messages non-acquittés sont donc les messages que RabbitMQ a déjà distribué à des consommateurs qui n'ont pas confirmé les avoir traités (dans le cas de celery, par défaut, l'acquittement se fait même avant de bosser).
Les messages prêts sont ceux qui n'ont pas encore été distribués à des consommateurs par RabbitMQ par absence de consommateurs disponibles.
Comment voir le contenu (payload) des messages d'une file d'attente ? Il faut utiliser le plugin management. Il met à disposition une API sur laquelle tapent une interface web (qui affiche de zolis graphes de charge) et l'outil en ligne de commande rabbitmqadmin
.
Pour activer le plugin : rabbitmq-plugins enable rabbitmq_management
. Pour le désactiver : rabbitmq-plugins disable rabbitmq_management
.
Récupérer le contenu des messages :
rabbitmqadmin -V <virtual_host> -u <identifiant> -p <mdp> get queue=<nom_file_attente> count=<nombre_de_messages_qu'on_veut_voir> requeue=true
. Si tu as « Access refused: /api/queues/%2F/nom_file/get », c'est que t'as oublié de préciser le nom de l'hôte virtuel ou un couple identifiant/mdp valide (le mieux est de reprendre celui utilisé par ton application ;) ) ;Seuls les messages prêts (ready) sont concernés (les messages unacknowledged sont considérés comme ayant été consommés).
L'affichage se fait du plus ancien (le premier arrivé dans la file d'attente) au plus récent (le dernier arrivé).
Si l'on ne précise pas « requeue=true », les messages sont sortis de la file d'attente, donc ils ne seront pas traités par les consommateurs !
Si l'on ne précise pas « count=X », un seul message est affiché (le plus récent).