直接用 delay() 就够了,它是 apply_async() 的语法糖;需控制队列、超时等调度参数时必须用 apply_async()。

FastAPI调用Celery任务时,delay() 和 apply_async() 选哪个
直接用 delay() 就够了,它是 apply_async() 的语法糖,等价于 apply_async(args=(), kwargs={})。但如果你需要控制队列、超时、重试或优先级,就必须用 apply_async()。
常见错误是以为 delay() 能传 queue 参数——它不能。传了会静默忽略,任务照常发到默认队列,但你可能根本没起对应队列的 worker。
-
delay(1, 2)→ 简单场景,够用 -
apply_async(args=(1, 2), queue="high_priority", countdown=5)→ 需要调度控制时必须用这个 - 如果用了
queue却没在 worker 启动时指定--queues,worker 会跳过该任务,日志里只显示 "Received unregistered task",容易误判为代码问题
Redis 作 broker 和 backend 时,broker 和 backend 的 DB 编号为什么建议分开
Broker(消息队列)负责暂存待执行任务,Backend(结果后端)负责存储任务返回值和状态。共用同一个 Redis DB 会导致 key 冲突、数据互相覆盖,尤其在高并发或清理缓存时风险极高。
比如你执行 redis-cli -n 0 FLUSHDB 清 broker,结果 backend 也跟着丢了;或者 Celery 内部用的 key 前缀(如 _kombu.binding)和你存的业务 key 撞上,worker 直接罢工。
FastAPI + Flask 混合部署最佳实践,解决路由定义、API 代理等常见问题,适用于同时运行 FastAPI API 与 Flask 前端的场景。
- 推荐配置:
broker='redis://localhost:6379/0',backend='redis://localhost:6379/1' - 不要图省事写成
redis://localhost:6379/0两边都用 —— 开发环境跑得通,上线后某次 flush 就出事 - 若用 Docker Compose,确保两个服务指向不同 DB,而不是靠容器名混淆
启动 Celery worker 时,--queues 参数漏配会怎样
Celery worker 默认只监听 celery 这个默认队列。如果你在 apply_async() 里指定了 queue="high_priority",但启动 worker 时没加 --queues high_priority,任务永远卡在 Redis 里,不会被消费。
现象是:FastAPI 接口秒回 {"task_id": "xxx"},但查 result.get() 一直超时,Redis 中 lrange celery:high_priority 0 -1 能看到任务还在,worker 日志里却毫无痕迹。
- 正确启动方式:
celery -A app.celery_app worker --loglevel=info --queues default,high_priority - 多个队列用英文逗号分隔,不能有空格
- 开发时建议加
--pool=solo避免 multiprocessing 导致的 fork 问题(尤其 macOS / Windows)
FastAPI 返回 task_id 后,前端怎么安全轮询结果
别让前端无限制轮询 /tasks/{task_id}/status。Celery 的 AsyncResult(task_id).state 是实时查 Redis,高频请求会把 Redis 打满,且无法感知任务是否真正失败(比如 worker 崩溃、网络中断)。
更稳妥的做法是:FastAPI 接口只返回 task_id,由后端异步写入一张任务状态表(带 created_at、updated_at、timeout_seconds),前端轮询这个表,后端用 Celery beat 定期扫描超时任务并标记为 FAILED。
- 轮询间隔建议从 1s 起步,逐步退避(如 1s → 2s → 5s → 10s),避免雪崩
- 务必设最大轮询次数或总超时时间,否则用户关掉页面,前端还在发请求
-
AsyncResult(task_id).get(timeout=30)只适合调试,生产环境禁止在 FastAPI 请求中调用——它会阻塞整个 ASGI worker

















