Celery任务不执行且无worker日志,大概率是BROKER_URL配置错误或Redis/RabbitMQ服务未运行;Docker中localhost指向错误,需用服务名;应使用@shared_task而非@app.task;Admin中避免同步调用,应异步触发并轮询状态;worker需合理设置并发与预取策略,并确保幂等性。

为什么 celery 启动后任务不执行,连 worker 日志都看不到?
大概率是 BROKER_URL(或 CELERY_BROKER_URL)配置错,或者 Redis/RabbitMQ 服务根本没跑起来。Celery worker 启动时只校验连接是否通,不校验权限或队列是否存在,所以连不上 broker 时可能静默失败——尤其在 Docker 或远程部署时,localhost 指的不是你以为的那个 localhost。
- 用
redis-cli -h your-redis-host -p 6379 ping或curl -I http://your-rabbitmq:15672先确认中间件可达 - Django 项目里检查
CELERY_BROKER_URL:本地开发用redis://127.0.0.1:6379/0,Docker Compose 部署必须换为服务名,比如redis://redis:6379/0 - 启动 worker 时加
--loglevel=info,避免默认warning级别吞掉关键提示 - 如果用 RabbitMQ,确保 vhost 存在且用户有
configure/write/read权限,否则任务会进黑洞
@shared_task 和 @app.task 在 Django 里该选哪个?
必须用 @shared_task。Django 项目里直接 import celery 实例再用 @app.task,会导致 task 注册延迟、序列化失败,甚至多进程下重复注册。因为 Celery 实例初始化依赖 Django settings 加载完成,而 @shared_task 是惰性代理,等真正 import tasks 模块时才绑定到实际 app。
- 在
tasks.py里写from celery import shared_task,然后@shared_task - 不要在
__init__.py或apps.py里提前 import tasks 模块,否则可能触发循环导入 - 如果需要自定义队列或重试策略,用
@shared_task(queue='high_priority', autoretry_for=(Exception,), retry_kwargs={'max_retries': 3}) - 函数参数必须可序列化:避免传
Model实例,改传id,进 task 再MyModel.objects.get(id=xxx)
Django Admin 里点击就发任务,但生产环境总超时或报 ConnectionRefusedError
这是典型的同步阻塞调用反模式。Admin 的 request/response 周期短,不该直接 my_task.delay() 后干等结果;更不该在视图里用 .get() 等任务返回——Celery 默认不支持同步等待,强行等会卡住整个 Web 进程。
- Admin 动作里只做
my_task.delay(obj.id),立刻返回 HTTP 200,前端靠轮询或 WebSocket 查状态 - 任务状态存到数据库字段(如
task_status)或 Redis hash,避免查 Celery 自带的AsyncResult(它依赖 broker,不稳定) - Web 进程和 worker 进程不能共用一个 Redis DB:Web 用 db 0 存 session,worker 用 db 1 存 broker,否则高并发时 key 冲突或被误清
- 如果必须返回结果,用
cache.set('task_result_123', data, timeout=300),前端按 ID 查缓存
上线后任务堆积、延迟飙升,flower 显示大量 received 但无 started
说明 worker 消费不过来,常见于单 worker 进程处理耗时 I/O(比如发邮件、调外部 API),或没有合理设置并发数。Celery 默认用 solo pool(单线程),CPU 密集型任务反而更慢。
立即学习“Python免费学习笔记(深入)”;
- 启动时指定并发:
celery -A myproject worker --concurrency=4 --pool=prefork(CPU 密集)或--pool=gevent -c 100(I/O 密集) - 用
CELERY_TASK_ACKS_LATE = True,防止 worker 崩溃导致任务丢失;同时设CELERY_WORKER_PREFETCH_MULTIPLIER = 1,避免预取太多压垮内存 - 监控
celery inspect active和celery inspect reserved,看是否有任务卡在 reserved 状态(说明 prefetch 太大或 worker 假死) - 别把数据库连接、文件句柄写在 task 函数顶层——它们会在 worker 进程启动时初始化,而不是每次任务执行时,容易复用过期连接
最麻烦的其实是任务幂等性:网络抖动导致重复投递、worker 重启重发、人为多次触发……这些不会报错,但数据会错,得在 task 开头加去重逻辑,比如用 Redis SETNX 锁住 task_type:arg_hash,5 分钟过期。


















