编写背景
task进入队列后,部分任务出现Failure或者一直Pending,且业务代码没有报错。
运行环境
celery配置
from celery import Celery
broker = 'redis://:@127.0.0.1:6379/1'
backend = 'redis://:@127.0.0.1:6379/2'
app = Celery(broker=broker,backend=backend,include=['celery_task.tasks'])
task.py
from celery_task.celery import app
from celery.result import AsyncResult
@app.task
def your_task():
# your task
pass
@app.task
def check_task(task_id):
# task status
status = AsyncResult(task_id).status
return status
celery部署方式
docker 部署
version: '3'
services:
services_name: test
image:test
#..... 省略
celery:
# ......省略
command: celery --app=celery_task worker -P gevent -l INFO
服务单台机器部署如上所示,当然,这里部署了多台机器。就是说,有多个celery worker能接收任务。
任务以redis为支撑,进入队列的任务。部分任务通过AsyncResult查询结果为Failure,且一直无法重试成功。
解决思路
初步怀疑任务执行失败的原因如下:
1.业务代码存在BUG,出现报错
2.celery任务负载过大,任务在队列等待时间过长。
3.redis 库号有其它服务在使用
经过日志排查剔除了 1 跟2。此时深度怀疑,有其他Celery服务用了跟这个队列一样的redis库号。
验证:
通过 CLIENT LIST查看所有连接redis,这里为了方便进行编码
import redis
# 连接Redis服务器
r = redis.Redis(host='127.0.0.1', port=6379, db=1)
# 获取客户端连接列表
client_list = r.client_list()
a = []
c = []
# 打印连接列表
for client in client_list:
ip = client['addr'].split(":")[0]
db = client["db"]
res = ip+":"+db
# 查看与celery队列同一库号的所有ip连接
if res not in c and db=="1":
c.append(res)
if res not in a and db=="2":
a.append(res)
# 这里结果不做展示
print(a,c)
此时发现,除了本服务的ip,还有另外的IP一样用了同样的库号。大可断定,进入队列的任务,是被其他worker分走了,所以任务一直Failure。
看下面一张图就明朗了:
这里假设redis分库为4个。然后有两个Celery在运行,但是里面的Worker是不一样的,两个Celery却用了同一个库号。因此,本应在Celery A执行的任务,会被分配到Celery B的worker上执行。被分配到Celery B 的任务就会Failure。