Polydesk-logotype
Polydesk.ai — Header

Message Queue

Une message queue (file de messages) est un composant d’architecture logicielle qui stocke temporairement des messages envoyés par un producteur et les délivre à un seul consommateur, permettant ainsi une communication asynchrone, découplée et résiliente entre les différentes parties d’un système distribué.

Message Queue en bref
Type
Composant de messagerie asynchrone
Modèle
Point-to-point (un message → un consommateur)
Garantie
FIFO, at-least-once ou exactly-once selon l’outil
Outils majeurs
RabbitMQ, Amazon SQS, Apache Kafka, Celery, BullMQ
Protocoles
AMQP, MQTT, STOMP, JMS
Usage IA
Inférence asynchrone, pipelines ML, task queues pour entraînement

Comment fonctionne une message queue

Le principe est celui d’une file d’attente. Un composant (le producteur) place un message dans la queue. Un autre composant (le consommateur) retire ce message de la queue et le traite. Entre les deux, le broker stocke les messages et garantit leur délivrance.

Le producteur n’attend pas que le consommateur traite le message. Il l’envoie et passe à la suite. Le consommateur récupère les messages à son propre rythme. Si le consommateur est temporairement indisponible, les messages s’accumulent dans la queue en attendant son retour. C’est cette asynchronicité qui rend le pattern si utile.

Le cycle de vie d’un message

  1. Le producteur crée un message (payload JSON, binaire, texte) et l’envoie à une queue nommée sur le broker.
  2. Le broker stocke le message dans la queue, en mémoire ou sur disque selon la configuration.
  3. Un consommateur se connecte au broker et demande le prochain message disponible (mode pull) ou le broker pousse le message vers le consommateur (mode push).
  4. Le consommateur traite le message.
  5. Le consommateur envoie un accusé de réception (ACK) au broker pour confirmer que le traitement est terminé.
  6. Le broker supprime le message de la queue.
  7. Si aucun ACK n’est reçu dans le délai imparti (timeout), le broker remet le message dans la queue pour qu’un autre consommateur le traite.
Point-to-point vs fanout La différence clé avec le Pub/Sub : dans une message queue, chaque message est consommé par un seul consommateur. Si trois workers écoutent la même queue, le broker distribue chaque message à un seul d’entre eux. C’est du load balancing, pas du broadcast.

Garanties de livraison

Les message queues offrent différents niveaux de garantie, et comprendre la distinction est crucial pour concevoir un système fiable.

La livraison at-most-once signifie que le message est envoyé une seule fois sans vérification. Si le consommateur plante, le message est perdu. C’est le mode le plus rapide mais le moins fiable.

La livraison at-least-once est le standard de la plupart des brokers. Le message est relivré si aucun ACK n’est reçu. Le risque : le consommateur peut recevoir le même message deux fois (si le traitement a réussi mais que l’ACK s’est perdu). Vos consommateurs doivent donc être idempotents.

La livraison exactly-once garantit que chaque message est traité une et une seule fois. C’est la garantie la plus forte mais aussi la plus coûteuse en performance. Amazon SQS FIFO et Kafka (avec la configuration appropriée) proposent ce mode.


Message Queue vs Pub/Sub

La confusion est fréquente. Voici la distinction nette.

Une message queue distribue chaque message à un seul consommateur. C’est conçu pour la distribution de tâches et le load balancing. Trois workers sur une queue = chaque tâche est traitée une seule fois.

Le Pub/Sub diffuse chaque message à tous les subscribers abonnés au topic. C’est conçu pour la notification et le fanout. Trois services sur un topic = les trois reçoivent le message.

Critère Message Queue Pub/Sub
Distribution Un message → un consommateur Un message → tous les subscribers
Pattern Load balancing / distribution de tâches Fanout / notification
Persistance Jusqu’à consommation + ACK Variable (fire-and-forget ou durable)
Ordre Généralement FIFO Non garanti par défaut
Cas d’usage typique Tâches en arrière-plan, inférence IA Événements, notifications, streaming
Exemple concret 5 workers GPU traitent une file d’images 3 services réagissent à « commande créée »

En pratique, les brokers modernes combinent les deux. Apache Kafka offre du Pub/Sub (plusieurs consumer groups reçoivent tous les messages) et du message queue (au sein d’un consumer group, chaque message va à un seul consommateur). RabbitMQ supporte les queues classiques et les exchanges fanout pour le Pub/Sub.


Les principaux outils de message queuing

RabbitMQ

RabbitMQ est le broker de messages open source le plus populaire. Il implémente le protocole AMQP (Advanced Message Queuing Protocol) et offre une flexibilité remarquable dans le routage des messages via ses « exchanges » (direct, fanout, topic, headers). Sa force : la fiabilité. Les messages peuvent être persistés sur disque, les ACK sont configurables, et les queues supportent la réplication pour la haute disponibilité.

RabbitMQ est le choix de référence pour les architectures de microservices de taille moyenne. Il gère sans problème des dizaines de milliers de messages par seconde. Pour des millions de messages par seconde, Kafka est plus adapté.

Amazon SQS

Amazon Simple Queue Service est un service de file de messages entièrement managé. Vous n’avez rien à installer, rien à configurer côté infrastructure. SQS propose deux types de queues : les queues standard (débit quasi illimité, ordre approximatif) et les queues FIFO (ordre garanti, exactly-once, limité à 300 transactions par seconde, ou 3 000 avec batching). Le tarif est d’environ $0,40 par million de requêtes. Pour les premiers 1 million de requêtes par mois, c’est gratuit.

Apache Kafka

Kafka n’est pas une message queue au sens traditionnel, c’est un log distribué. Les messages sont écrits dans des partitions ordonnées et conservés pendant une durée configurable (pas supprimés à la consommation). Au sein d’un consumer group, chaque partition est assignée à un seul consommateur, ce qui crée un comportement de message queue. Kafka excelle pour le streaming à très haut débit (millions de messages par seconde) et l’event sourcing.

Celery (Python)

Celery n’est pas un broker mais une bibliothèque de task queue. Elle s’appuie sur un broker externe (Redis ou RabbitMQ) pour stocker les messages. Celery est le standard de facto en Python pour les tâches asynchrones : envoi d’emails, traitement d’images, entraînement de modèles ML, inférence IA par batch. Elle offre les retries automatiques, le scheduling (via Celery Beat), le monitoring (via Flower), et la distribution sur plusieurs machines.

# Définir une tâche Celery pour l'inférence IA
from celery import Celery

app = Celery('ia_tasks', broker='redis://localhost:6379/0')

@app.task(bind=True, max_retries=3, default_retry_delay=30)
def run_inference(self, model_name, input_data):
    try:
        model = load_model(model_name)
        result = model.predict(input_data)
        save_result(result)
        return {"status": "success", "prediction": result}
    except Exception as exc:
        raise self.retry(exc=exc)

# Envoyer une tâche dans la queue
run_inference.delay("fraud-detection-v3", {"amount": 4500})

BullMQ (Node.js)

BullMQ est l’équivalent de Celery pour l’écosystème Node.js. Il s’appuie sur Redis et offre le scheduling, les jobs parent-enfant avec dépendances, le retry avec backoff exponentiel, et les queues de priorité. BullMQ est utilisé pour le transcodage vidéo, les pipelines IA, et le traitement de paiements dans des entreprises de toutes tailles.

Redis Streams

Redis Streams combine les fonctionnalités d’un log append-only (comme Kafka) et de consumer groups pour le load balancing. C’est une alternative légère à Kafka pour les cas d’usage où vous avez déjà Redis dans votre stack et n’avez pas besoin de la scalabilité massive de Kafka. Contrairement à Redis Pub/Sub (fire-and-forget), Redis Streams persiste les messages.

Comparaison des outils

Outil Type Protocole Débit max Persistance Écosystème
RabbitMQ Open Source Broker complet AMQP, MQTT, STOMP ~50K msg/s Configurable Universel
Amazon SQS Managed queue HTTP/REST Quasi illimité (standard) Oui AWS
Apache Kafka Open Source Log distribué Kafka protocol Millions msg/s Oui (durable) Universel
Celery Open Source Task queue (lib) Via broker (Redis/AMQP) Dépend du broker Via broker Python
BullMQ Open Source Task queue (lib) Redis ~10K jobs/s Oui (Redis) Node.js, Python, Elixir
Redis Streams Open Source Log léger Redis protocol ~100K msg/s Oui Universel

Message Queue dans l’écosystème IA

Les message queues sont omniprésentes dans les pipelines d’IA. Dès que vous avez une tâche qui prend plus de quelques secondes (inférence sur un gros modèle, traitement d’un document, génération d’image), vous ne pouvez pas bloquer la requête HTTP en attendant le résultat. La message queue découple la soumission de la tâche de son exécution.

Inférence asynchrone

Le cas d’usage le plus courant : un utilisateur soumet une requête (générer une image, analyser un document, transcrire un audio), votre API place la tâche dans une queue, et un worker GPU la traite en arrière-plan. Le résultat est renvoyé via webhook, notification, ou stocké pour consultation ultérieure.

Ce pattern est celui de Replicate, RunPod, et la plupart des plateformes d’inférence : l’appel API retourne immédiatement un identifiant de tâche, et le client interroge périodiquement (ou reçoit un callback) pour récupérer le résultat. Les APIs d’OpenAI avec le background mode, le batch processing d’Anthropic, et les endpoints asynchrones de Replicate fonctionnent tous sur ce principe.

# Architecture typique d'inférence asynchrone avec Celery
from celery import Celery
from transformers import pipeline

app = Celery('inference', broker='redis://localhost:6379/0')

# Worker GPU : traite les tâches de la queue
@app.task
def generate_summary(document_text):
    summarizer = pipeline("summarization", model="facebook/bart-large-cnn")
    result = summarizer(document_text, max_length=150)
    return result[0]["summary_text"]

# API FastAPI : place la tâche dans la queue
from fastapi import FastAPI
api = FastAPI()

@api.post("/summarize")
async def summarize(doc: dict):
    task = generate_summary.delay(doc["text"])
    return {"task_id": task.id, "status": "queued"}

@api.get("/result/{task_id}")
async def get_result(task_id: str):
    result = generate_summary.AsyncResult(task_id)
    if result.ready():
        return {"status": "done", "summary": result.get()}
    return {"status": "processing"}

Pipelines d’entraînement

L’entraînement de modèles ML implique souvent une chaîne de tâches : préparation des données, augmentation, entraînement, évaluation, déploiement. Chaque étape peut être une tâche dans une queue, avec des dépendances entre elles. Celery permet de chaîner des tâches avec chain() et de créer des workflows complexes avec group() (parallélisme) et chord() (parallélisme puis agrégation).

from celery import chain, group

# Pipeline séquentiel : préparer → entraîner → évaluer → déployer
pipeline = chain(
    prepare_data.s(dataset_id="ds-001"),
    train_model.s(epochs=10, lr=0.001),
    evaluate_model.s(test_set="validation"),
    deploy_model.s(env="staging")
)
pipeline.delay()

# Entraîner 3 variantes en parallèle, puis comparer
from celery import chord
variants = group(
    train_model.s(lr=0.001),
    train_model.s(lr=0.0001),
    train_model.s(lr=0.01)
)
workflow = chord(variants)(compare_results.s())
workflow.delay()

Scaling des workers GPU

Un avantage majeur des message queues pour l’IA : le scaling horizontal. Vous avez 100 images à traiter et 2 GPUs ? Chaque GPU lance un worker Celery qui consomme la queue. Le broker distribue automatiquement les tâches. Quand la charge augmente, vous ajoutez des workers. Quand elle diminue, vous les retirez. Les tâches non traitées restent dans la queue.

AWS Batch utilise exactement ce pattern : des workers Celery ou BullMQ tournent sur des instances GPU provisionnées dynamiquement, consommant les tâches d’une queue SQS ou Redis.

Agents IA et orchestration de tâches

Les systèmes d’agents IA utilisent les message queues pour distribuer le travail entre agents spécialisés. Un agent de planification décompose une tâche complexe en sous-tâches et les place dans différentes queues. Des agents spécialisés (recherche web, exécution de code, analyse de données) consomment leurs queues respectives. Les résultats sont renvoyés via une queue de retour vers l’agent de planification qui agrège les résultats.

Ce pattern est distinct du Pub/Sub event-driven utilisé pour la coordination globale : la message queue sert à distribuer des tâches spécifiques à des workers spécialisés, tandis que le Pub/Sub diffuse des événements à tous les agents intéressés.

Celery vs BullMQ : quel choisir ? Si votre stack est Python (FastAPI, Django, scripts ML), Celery est le choix évident. Il est mature, bien documenté, et intégré dans tout l’écosystème scientifique Python. Si vous travaillez en Node.js (Express, Next.js), BullMQ est l’alternative naturelle avec un excellent support des jobs hiérarchiques. Pour des besoins cross-langage, préférez un broker comme RabbitMQ ou SQS que vous consommerez avec le client natif de chaque langage.

Avantages et limites

Avantages

Le premier bénéfice est la résilience. Si un consommateur plante, le message reste dans la queue. Quand le consommateur redémarre, il reprend où il s’est arrêté. Aucune donnée n’est perdue. C’est une assurance pour vos pipelines de données et d’inférence.

Le load leveling est un autre atout majeur. Lors d’un pic de trafic, les requêtes s’accumulent dans la queue plutôt que de submerger vos workers. Le système traite les messages à son propre rythme, lissant les pics de charge. Pour les applications IA où l’inférence est coûteuse en ressources GPU, c’est indispensable.

Le découplage permet à vos producteurs et consommateurs d’évoluer indépendamment. Votre API web peut être en Python, vos workers d’inférence en Rust pour la performance, et votre service d’envoi d’emails en Node.js. Ils communiquent tous via la queue sans connaître les détails d’implémentation des autres.

Le scaling horizontal est natif. Ajoutez des workers = augmentez le débit de traitement. Retirez des workers = réduisez les coûts. La queue absorbe la différence.

Limites

La latence additionnelle est inhérente au pattern. Un appel synchrone prend quelques millisecondes. Un message qui passe par une queue ajoute le temps de sérialisation, d’écriture dans le broker, de lecture par le consommateur, et du traitement. Pour les opérations qui nécessitent une réponse immédiate (autocomplétion, streaming de tokens), les message queues ne sont pas adaptées.

La complexité opérationnelle augmente. Vous devez maintenir un broker (ou payer un service managé), gérer les dead letter queues pour les messages en échec, monitorer le lag des consommateurs, et gérer les cas de messages dupliqués. C’est un composant supplémentaire dans votre infrastructure.

Le debugging est moins direct qu’avec des appels synchrones. Tracer un message depuis la soumission jusqu’au traitement nécessite des identifiants de corrélation et du distributed tracing (Langfuse, Jaeger, ou similaire).

Ne pas utiliser votre base de données comme message queue C’est une erreur classique : créer une table « jobs » dans PostgreSQL et la poller périodiquement. Cela fonctionne à petite échelle, mais pose des problèmes de verrouillage, de performance sous charge, et de « phantom reads » à mesure que le volume augmente. Utilisez un outil conçu pour ça (SQS, Redis, RabbitMQ). L’investissement en complexité est minime par rapport aux problèmes évités.

Bonnes pratiques

Rendre les consommateurs idempotents

Avec la livraison at-least-once, un message peut arriver deux fois. Votre consommateur doit produire le même résultat s’il traite deux fois le même message. La technique standard : stocker l’identifiant du message dans une table de déduplication et ignorer les doublons.

Configurer des dead letter queues

Quand un message échoue après N tentatives (retries), il doit être redirigé vers une dead letter queue pour analyse humaine plutôt que de bloquer la queue principale. Configurez un nombre maximum de retries (typiquement 3 à 5) avec un backoff exponentiel entre chaque tentative.

Garder les messages légers

Ne mettez pas un fichier de 50 Mo dans un message. Stockez le fichier dans un object store (S3, GCS) et ne mettez que la référence (URL ou identifiant) dans le message. Les brokers sont optimisés pour des messages de quelques Ko, pas pour du transfert de fichiers.

Monitorer le consumer lag

Le « lag » (nombre de messages en attente dans la queue) est votre indicateur principal. Un lag qui augmente régulièrement signifie que vos consommateurs ne suivent pas le rythme des producteurs. C’est le signal pour scaler horizontalement (ajouter des workers) ou optimiser le traitement.

Définir des timeouts réalistes

Pour les tâches d’inférence IA, les timeouts doivent être adaptés. Un modèle de génération d’image peut prendre 30 secondes, un deep research plusieurs minutes. Configurez les timeouts du broker et des workers en conséquence, avec une marge de sécurité.


Verdict

La message queue est le pattern fondamental pour tout traitement asynchrone, et particulièrement pour les workloads IA où l’inférence et l’entraînement sont coûteux en temps et en ressources. Si vous construisez une application qui appelle un LLM, traite des images, ou orchestre des agents IA, vous aurez besoin d’une message queue tôt ou tard.

Pour un projet Python, commencez avec Celery + Redis. C’est le combo le plus simple à mettre en place et le mieux documenté. Pour du Node.js, BullMQ + Redis fait le même travail. Pour de la production à grande échelle sur AWS, SQS est imbattable en simplicité opérationnelle. Et si vous avez besoin de combiner message queue et Pub/Sub dans un même système, Kafka ou RabbitMQ couvrent les deux cas.


Questions fréquentes sur les message queues

Quelle est la différence entre une message queue et le Pub/Sub ?

Dans une message queue, chaque message est traité par un seul consommateur (distribution de tâches). En Pub/Sub, chaque message est diffusé à tous les subscribers (notification). Utilisez une message queue pour distribuer du travail entre workers, et du Pub/Sub pour notifier plusieurs services d’un même événement. Les brokers modernes comme Kafka et RabbitMQ supportent les deux patterns.

Pourquoi les pipelines d’IA utilisent-ils des message queues ?

L’inférence IA est souvent lente (quelques secondes à plusieurs minutes) et gourmande en ressources GPU. Une message queue permet de découpler la soumission de la requête de son traitement : l’API répond instantanément avec un identifiant de tâche, et le worker GPU traite à son rythme. Cela permet aussi le scaling horizontal (ajouter des workers) et la résilience (si un worker plante, le message reste dans la queue).

Celery ou BullMQ : lequel choisir ?

Celery est le standard pour Python (Django, FastAPI, scripts ML). BullMQ est l’équivalent pour Node.js. Si votre stack est Python et que vous faites du ML, Celery est le choix naturel. Si vous êtes en Node.js, prenez BullMQ. Les deux utilisent Redis comme broker et offrent des fonctionnalités similaires (retries, scheduling, monitoring).

Peut-on utiliser une base de données comme message queue ?

Techniquement oui, en pratique c’est déconseillé. Une table « jobs » avec polling fonctionne à faible volume, mais pose des problèmes de verrouillage, de performance sous charge, et de consommation CPU inutile. Les brokers dédiés (Redis, SQS, RabbitMQ) sont conçus pour ce cas d’usage avec des mécanismes de notification push, de persistance optimisée, et de gestion des ACK intégrés.

Combien coûte Amazon SQS ?

Amazon SQS offre 1 million de requêtes gratuites par mois. Au-delà, le tarif est d’environ $0,40 par million de requêtes pour les queues standard et $0,50 par million pour les queues FIFO. Le transfert de données est facturé séparément. Pour la plupart des applications de taille moyenne, le coût reste négligeable comparé aux instances de calcul.

Polydesk.ai — Footer