Tôt ou tard, toute application web Python doit exécuter des tâches en dehors du cycle requête-réponse : envoyer un email après avoir renvoyé la réponse, générer un rapport, traiter un fichier téléversé, relancer un appel tiers instable. Exécuter ces traitements de manière synchrone rend l'application lente et fragile ; la bonne approche consiste à utiliser un système de tâches d'arrière-plan. La difficulté, c'est de choisir le bon. Celery, RQ, Dramatiq et APScheduler permettent tous d'exécuter des tâches en arrière-plan, mais ils répondent à des problèmes différents. Ce guide les compare de manière honnête pour vous aider à choisir l'outil adapté du premier coup.
Comment choisir en toute confiance
- Le modèle de base : un courtier, une file d'attente et des processus worker
- Celery : la solution historique, puissante et riche en fonctionnalités, mais plus lourde
- RQ : l'option simple, exclusivement Redis, facile à appréhender
- Dramatiq : un juste milieu moderne avec d'excellents paramètres par défaut
- APScheduler : pour la planification, un problème complètement différent
Info
Tout d'abord, distinguez deux besoins différents
Les files de tâches (Celery, RQ, Dramatiq) exécutent des travaux déclenchés par votre application (« traite ceci maintenant, en dehors du chemin de la requête »). Les planificateurs (APScheduler) exécutent des travaux déclenchés par le temps (« fais ceci tous les soirs à 2 h »). De nombreuses applications ont besoin des deux, et les confondre est à l'origine de beaucoup de confusion.
Le modèle mental partagé
Toutes les files de tâches partagent la même structure. Votre application place un job (un nom de fonction accompagné de ses arguments) dans un courtier, généralement Redis ou RabbitMQ. Des processus worker distincts extraient les jobs de la file et les exécutent, en gérant les nouvelles tentatives, les échecs et les résultats. Votre processus web reste rapide car il ne fait que placer les jobs en file ; le travail lent s'effectue ailleurs.
web app ──enqueue──▶ [ BROKER: Redis/RabbitMQ ] ──▶ worker pool ──▶ done
(fast) holds the queue (does the slow work)
Same shape for Celery, RQ, and Dramatiq.
They differ in features, simplicity, and operational weight.
Celery : la solution historique riche en fonctionnalités
Celery est le choix par défaut de longue date, le plus puissant et le plus largement déployé. Il prend en charge plusieurs courtiers, des workflows complexes (chaînes, groupes, accords), des tâches planifiées via son planificateur beat, la limitation de débit, les nouvelles tentatives et une surface de configuration étendue. Cette puissance a un coût : Celery a la réputation d'être complexe à configurer et de présenter des pièges opérationnels, et son ampleur peut être écrasante pour un besoin simple.
# tasks.py
from celery import Celery
app = Celery("app", broker="redis://localhost:6379/0", backend="redis://localhost:6379/1")
@app.task(bind=True, max_retries=3, autoretry_for=(ConnectionError,), retry_backoff=True)
def send_receipt(self, order_id: int) -> None:
order = Order.objects.get(id=order_id) # pass an ID, not the object
email.send(order.email, render_receipt(order))
# Enqueue from your web app — returns instantly, work happens on a worker.
send_receipt.delay(order_id=42)
# Run workers: celery -A tasks worker --concurrency=8
# Periodic: celery -A tasks beat (Celery's built-in scheduler)
✓ Avantages
- Le plus de fonctionnalités, le plus grand écosystème et la plus grande communauté
- Workflows complexes : chaînes, groupes, accords, rappels
- Plusieurs backends de courtier ; mature, éprouvé à grande échelle
- Planification périodique intégrée via beat
✕ Inconvénients
- La complexité de configuration est réelle, facile à mal configurer
- Plus lourd à opérer que les alternatives plus simples
- Surdimensionné quand on a juste besoin d'exécuter une fonction plus tard
- Le débogage de ses cas limites demande un certain apprentissage
RQ : la simplicité par conception
RQ (Redis Queue) est l'antithèse de l'ampleur de Celery : il est petit, lisible et ne fait qu'une chose : exécuter des fonctions Python sur des workers via Redis. Si vous pouvez lire son code source en un après-midi (c'est presque possible), vous pouvez comprendre son comportement en production. Le compromis est moins de fonctionnalités et une dépendance exclusive à Redis, mais pour une grande partie des applications, c'est exactement ce qu'il faut.
from redis import Redis
from rq import Queue
# Any plain function is a job — no decorator required.
def send_receipt(order_id: int) -> None:
order = Order.objects.get(id=order_id)
email.send(order.email, render_receipt(order))
queue = Queue(connection=Redis())
queue.enqueue(send_receipt, 42, retry=Retry(max=3)) # enqueue and move on
# Run a worker: rq worker
✓ Avantages
- Minimal, facile à apprendre, facile à déboguer
- Redis uniquement, une dépendance que vous utilisez probablement déjà
- Comportement transparent ; peu de magie cachée
- Parfait pour les tâches simples de type « fais ceci plus tard »
✕ Inconvénients
- Moins de fonctionnalités avancées que Celery ou Dramatiq
- Redis seulement ; pas d'option RabbitMQ
- Les workflows complexes nécessitent un assemblage plus manuel
- Moins adapté aux pipelines très complexes à haut débit
Dramatiq : le juste milieu moderne
Dramatiq a été conçu pour conserver l'essentiel de l'utilité de Celery tout en étant plus simple et plus fiable par défaut. Il offre un comportement sensé dès le départ : nouvelles tentatives automatiques, limites d'âge des messages et fonctionnalités de fiabilité qu'il faut assembler ou paramétrer ailleurs. Pour les équipes qui trouvent Celery trop lourd mais RQ trop dépouillé, Dramatiq est souvent le point d'équilibre idéal.
import dramatiq
# Retries and sane defaults are built in; you opt into specifics per actor.
@dramatiq.actor(max_retries=3, time_limit=30_000)
def send_receipt(order_id: int) -> None:
order = Order.objects.get(id=order_id)
email.send(order.email, render_receipt(order))
send_receipt.send(42) # enqueue
# Run workers: dramatiq tasks
✓ Avantages
- API propre avec des paramètres par défaut fiables et sensés
- Nouvelles tentatives et gestion des messages intégrées et bien faites
- Prend en charge Redis et RabbitMQ
- Moins de surprises opérationnelles que Celery pour de nombreuses équipes
✕ Inconvénients
- Communauté plus petite que celle de Celery (bien que saine)
- Moins de primitives de workflow exotiques que Celery
- Nécessite toujours un courtier et des opérations worker comme les autres
- Moins omniprésent, donc moins d'intégrations prêtes à l'emploi
APScheduler : un outil différent pour un travail différent
APScheduler n'est pas une file de tâches, c'est un planificateur. Son rôle est d'exécuter du code sur un déclencheur temporel : toutes les heures, chaque jour de semaine à 9 h, dans trente minutes. C'est la réponse intégrée au processus pour « fais ceci selon un planning ». Il est important de noter qu'il ne fournit pas, à lui seul, un pool de workers distribué avec nouvelles tentatives et mise en file d'attente ; pour les travaux planifiés qui sont également lourds ou doivent passer à l'échelle, le modèle courant consiste à laisser un planificateur simplement placer des jobs dans une file de tâches.
from apscheduler.schedulers.background import BackgroundScheduler
from apscheduler.triggers.cron import CronTrigger
scheduler = BackgroundScheduler()
# Don't do the heavy work here — just drop a job onto the real task queue.
@scheduler.scheduled_job(CronTrigger(hour=2, minute=0)) # every day at 02:00
def nightly_reports():
for tenant_id in active_tenant_ids():
generate_report.delay(tenant_id) # Celery/RQ/Dramatiq does the actual work
scheduler.start()
Astuce
Planifiez, puis placez en file d'attente
Le modèle le plus propre pour les travaux récurrents lourds consiste à utiliser un planificateur (APScheduler, Celery beat ou même la cron système) pour ne rien faire d'autre que déposer un job dans votre file de tâches au bon moment. Le planificateur décide « quand » ; la file et ses workers gèrent le « comment » avec les nouvelles tentatives, la concurrence et la montée en charge.
| Dimension | Celery | RQ | Dramatiq | APScheduler |
|---|---|---|---|---|
| Type | File de tâches | File de tâches | File de tâches | Planificateur |
| Idéal pour | Puissance et écosystème | Simplicité | Équilibre moderne | Déclencheurs temporels |
| Courtier | Redis / RabbitMQ / + | Redis uniquement | Redis / RabbitMQ | Intégré au processus |
| Complexité | Élevée | Faible | Moyenne | Faible |
| Tentatives intégrées | Oui (configurable) | Basique | Oui (bons réglages par défaut) | Non applicable |
Guide de décision
Vous avez juste besoin d'exécuter une fonction plus tard, tout simplement ?
Optez pour RQ. Peu de pièces mobiles, Redis que vous avez probablement déjà, un comportement que vous maîtrisez parfaitement.
Vous voulez des valeurs par défaut modernes et de la fiabilité sans la lourdeur de Celery ?
Choisissez Dramatiq. Il atteint l'équilibre entre fonctionnalités et simplicité pour la plupart des applications en croissance.
Vous avez besoin de workflows complexes, de plusieurs brokers ou d'un écosystème maximal ?
Celery reste la réponse : acceptez le coût de configuration en échange de sa puissance.
Vous avez besoin d'une planification temporelle ?
Utilisez APScheduler (ou cron/Celery beat) et faites-lui simplement placer le job dans votre file d'attente pour tout ce qui est lourd.
les files d'attente exécutent du travail à la demande ; les planificateurs exécutent du travail basé sur le temps, choisissez selon le besoin
Réalités opérationnelles pour tous
Quel que soit votre choix, les mêmes réalités s'appliquent : rendez les tâches idempotentes, car elles s'exécuteront parfois plus d'une fois ; gardez les arguments de tâche petits et sérialisables (passez un ID, pas un objet volumineux) ; configurez les tentatives avec backoff pour les échecs transitoires et une file de lettres mortes pour les échecs permanents ; et surveillez la profondeur de votre file d'attente, car un arriéré qui s'accumule silencieusement est la façon dont les systèmes d'arrière-plan échouent de manière invisible.
✓ Avantages
- Tâches idempotentes qui tolèrent d'être exécutées deux fois
- Arguments petits et sérialisables, passez des ID, récupérez à l'intérieur de la tâche
- Tentatives avec backoff plus une destination de lettres mortes
- Surveillance de la profondeur de file, du taux d'échec et de la santé des workers
✕ Inconvénients
- Ne pas passer d'objets volumineux ou de connexions actives comme arguments de tâche
- Ne pas supposer une exécution exactement unique
- Pas de tentatives illimitées qui martèlent une dépendance défaillante
- Ne pas faire fonctionner une file d'attente sans visibilité sur son arriéré
! Erreurs courantes à éviter
-
✕Passer des objets volumineux ou des connexions actives comme arguments de tâche.
✓Passez un petit ID et récupérez à l'intérieur de la tâche ; les arguments doivent être petits et sérialisables.
-
✕Supposer qu'une tâche s'exécute exactement une fois.
✓Rendez les tâches idempotentes, elles s'exécuteront parfois plus d'une fois.
-
✕Tentatives illimitées qui martèlent une dépendance défaillante.
✓Réessayez avec backoff et envoyez les échecs permanents vers une file de lettres mortes.
-
✕Faire fonctionner la file d'attente sans visibilité sur son arriéré.
✓Surveillez la profondeur de file, le taux d'échec et la santé des workers : un arriéré silencieux est la façon dont ces systèmes échouent.
? Foire aux questions
Quelle est la différence entre une file de tâches et un planificateur ? +
Une file de tâches (Celery, RQ, Dramatiq) exécute du travail déclenché par votre application, en dehors du chemin de requête. Un planificateur (APScheduler) exécute du travail déclenché par le temps. De nombreuses applications ont besoin des deux: et le modèle propre est de faire en sorte que le planificateur place les jobs dans la file d'attente.
Celery vs RQ vs Dramatiq: lequel choisir ? +
RQ pour la simplicité (Redis uniquement, facile à raisonner), Dramatiq pour des valeurs par défaut modernes et de la fiabilité sans la lourdeur de Celery, et Celery pour les workflows complexes, plusieurs brokers et le plus grand écosystème.
Quand Celery est-il excessif ? +
Quand vous avez juste besoin d'exécuter une fonction plus tard. Sa puissance s'accompagne d'une complexité de configuration et d'angles vifs opérationnels qu'un outil plus simple comme RQ évite.
Comment exécuter un travail planifié lourd ? +
Utilisez un planificateur (APScheduler, Celery beat ou cron) pour ne rien faire d'autre que placer un job dans votre file de tâches au bon moment ; la file et ses workers gèrent les tentatives, la concurrence et la montée en charge.
Pourquoi les tâches doivent-elles être idempotentes ? +
Parce que la livraison au moins une fois signifie qu'une tâche peut s'exécuter deux fois (un worker peut planter après avoir fait le travail mais avant de l'acquitter). Les tâches idempotentes font d'une exécution en double une opération nulle inoffensive.
Succès
Adaptez l'outil à la forme du travail
Il n'y a pas d'option universellement meilleure : RQ pour la simplicité, Dramatiq pour l'équilibre moderne, Celery pour la puissance et l'écosystème, APScheduler pour les déclencheurs temporels. Identifiez si votre besoin est à la demande ou planifié, simple ou élaboré, et le bon choix devient évident. Appliquez ensuite les bases opérationnelles et votre système d'arrière-plan devient le cheval de trait fiable et invisible qu'il devrait être.
Pratiquez en déplacement
Apprenez Python, l'application Android gratuite
Chaque sujet de cette série est également dans l'application : leçons en bouchées, exemples exécutables, quiz, mini-projets et un environnement Python hors ligne qui fonctionne sur votre téléphone.
Commentaires
0Aucun commentaire pour l’instant. Soyez la première personne à donner votre avis.