Celery 5.4 跑视频转码任务,8 个 worker 各 4 并发,队列里 400 个任务,监控显示其中一个 worker 手里握着 200 个未确认任务,其余 worker 空闲。

一开始怀疑是「可见性超时太短,长任务被重新投递」,查了一圈,方向不对。

排查方向其实有限,先把可能性排一排。prefetch_multiplier 默认 4,长任务被整批预取。也可能是任务没有设置 acks_late,取走即视为已接收。

不容易想到的是,没有任何路由,所有任务进同一个队列。

第一件事是查出当前尚未确认的任务数

python -c "from celery import Celery;app=Celery(broker='redis://localhost:6379/0');print(app.control.inspect().active())"

随后把预取倍数调成 1 限制单 worker 占用

python -c "print('worker_prefetch_multiplier=1')"

再往下按任务耗时拆队列并给长任务单独 worker

python worker.py --queues video --concurrency 2 --prefetch 1

剩下的就是开启 acks_late 让任务在完成后才确认

python worker.py --acks-late --visibility-timeout 3600

别只看报错消失,验证要多做一步。各 worker 手上的未确认任务数接近均衡,队列积压下降速度提升,没有任务被重复执行。

这里还有个坑。预取是为了吞吐,长任务场景下会把队列压在一个 worker 上,预取倍数和确认时机要一起改。线上能稳定复现的问题都不算难,难的是只在大流量下才出现的那种。

作者 9823189