这类问题我又碰上了一次。Celery 5.4 跑图片标注任务,prefetch_multiplier 用默认值四,八个 worker 把一个队列的活全预取到本地,其中两个长任务卡住十分钟,其余 worker 全空闲,队列显示有积压却没人消费。
第一反应是「长短任务混在同一个队列里没有隔离」,验证下来不成立。
把日志和配置对完,能解释这个现象的基本就这几处。预取倍数让单个 worker 先占住多条消息;任务时长差异大,长任务把预取槽位占死;关闭 acks_late 后消息在预取阶段就出队了。
上来先看各 worker 的预取与未确认任务数
celery -A tasks inspect active_queues; celery -A tasks inspect reserved
接着把预取倍数改成 1 并限制单任务时长
celery -A tasks worker -c 4 --prefetch-multiplier 1 --time-limit 600
再往下按任务时长拆成两个队列分别消费
celery -A tasks worker -Q short -c 8 -l info
压测查看积压是否被均匀消化
python enqueue.py --n 500 --queue short; celery -A tasks inspect stats | Select-String total
恢复的判定其实很直接。两个队列的积压都在几分钟内清空,空闲 worker 数为零,长任务不再阻塞短任务,单条任务延迟下降一半。
这里还有个坑。预取是吞吐与多 worker 公平性之间的取舍,任务时长差异大时要把长短拆队列,而不是继续加 worker。改动前把原配置备份一份,出问题能马上回滚,这比任何技巧都实用。
