Celery故障处理
问题描述
某段时间, 正常运行的celery定时任务就会忽然停止运行, celery_beat
和 celery_worker
进程并无异常
首先做了以下排查
- task 是否抛出异常导致整个task没有产生正常结果?
- 消息队列是否异常导致task丢失?
- 是否性能问题导致task处理不过来?
在明确排除了以上原因后, 开始了深入的问题分析
分析
检查日志
查看日志发现celery_worker
最后接收了一些任务后就没有继续接收, 且这些任务并有正常返回result
, 定时器执行的时候, 发送出去的任务, 也是没有接收到
1 | (venv) [webapi@VM_231_194_centos log]$ tail -f worker_err.log |
此celery_worker 除了执行schedule调度的定时任务以外, 还有实时的异步任务例如发送邮件
检查状态
从stats
可以得出一些信息, worker
跑了4个子进程
1 | (venv) [webapi@VM_231_194_centos current]$ celery -A celery_worker:celery inspect stats |
通过reserved
查看目前接收等待处理的task
数量是16, 粗略地估算了一下task大小(与传递的数据相关), 16个任务大小已经接近64K
1 | (venv) [webapi@VM_231_194_centos current]$ celery -A celery_worker:celery inspect reserved | grep -v "OK" | wc -l |
通过active
找到目前worker
只有一个进行中的任务, 并且这个任务已经超时了非常长的时间
1 | (venv) [webapi@VM_231_194_centos current]$ celery -A celery_worker:celery inspect active | grep -v "OK" | wc -l |
思考过程
celery_worker
是多进程来接收处理任务, 返回任务的结果, 问题是出在active
的任务明明只有一个, 为什么所有子进程都没法处理其他已经接收的任务呢?这会不会是worker
的消费模型导致?从官网的资料入手, 里面有prefork-pool-prefetch-settings 说明
- worker 根据配置fork一些子进程
- 父进程通过消息队列获取任务
- 父进程通过管道向子进程发送任务
- 子进程接收处理任务并返回结果
prefectch-settings
是让父进程往pipe
里面异步发送task
, pipe
缓冲区大小默认64KB(部分系统可能是1MB), 如果子进程A执行长时间某个任务, 这时候pipe
来了一些task
是需要A接收的, 如果刚好缓冲区满了, 主进程发送给A时被阻塞那么所有其他子进程都无法通过pipe
接收任务
以下是伪代码演示
1 | -> send task T1 to process A |
流程图如下
1 | graph LR |
解决方案
worker
提供-Ofair
参数来禁用这种prefetch-settings
启动这个参数后, 主进程只会向当前可用的PIPE
发送数据
以下是伪代码演示
1 | -> send task T1 to process A |
上面只是避开来遇到长期阻塞的子进程,极端的情况下有可能全部子进程都被阻塞, 所以我们针对task
应该要有超时设置来保证这个错误能被正确处理. 由于问题是因为send_mail
这个任务引起, 所以我们要对这个任务进行时间限制, 只要超出来时间, 就自动抛出SoftTimeLimitExceeded
而不是无限期等待
1 |
|