giftiaのblog

celery多队列多任务的阻塞和并发问题

· 更新于 · giftia

为什么会阻塞

长短任务混杂

单个 Worker 处理多队列时,长任务会阻塞短任务。解决方案:隔离队列

# 不同队列用独立 Worker
celery -A tasks worker --queues=fast_tasks --concurrency=10 -n fast_worker@%h
celery -A tasks worker --queues=slow_tasks --concurrency=2 -n slow_worker@%h

预取机制

worker_prefetch_multiplier 默认 4,Worker 会一次拉取 并发数 × 4 个任务到本地缓存。长任务卡住后,其他 Worker 空闲也无法接手。

解决:对重任务队列 worker_prefetch_multiplier = 1

同步等待死锁

任务内部使用 .get() 同步等待另一个任务,并发占满时会导致死锁。

解决:使用 Celery Canvas 工作流:

from celery import chain, group

# 链式:串行执行
workflow = chain(
    validate.s(order_id),
    deduct.s(order_id),
    notify.s(order_id)
)

# 组:并行执行
group(tasks).apply_async()

并发模式选择

模式适用注意
Prefork(默认)CPU 密集型进程切换开销大
GeventI/O 密集型代码必须非阻塞
Solo严格串行并发数为 1

生产配置建议

# 全局超时
app.conf.task_time_limit = 300       # 硬超时 5 分钟
app.conf.task_soft_time_limit = 270  # 软超时 4.5 分钟

# 重任务队列:关闭预取
app.conf.worker_prefetch_multiplier = 1
app.conf.task_acks_late = True

十六字方针:长短分家、引擎选对、预取设小、超时设牢。