Celery 任务显示 SUCCESS,返回的却是另一个函数的结果
同模块的同名闭包或方法可能注册成同一个 Celery 任务。检查任务注册名,使用稳定的显式名称,并核对生产者、worker 与在途消息的兼容处理。
用工厂函数创建两个 Celery 任务,一个乘 2,一个乘 3,提交后都显示 SUCCESS,结果却全是乘 2。两个 Python 对象地址不同,也不能排除任务注册名冲突:消息交给 worker 时,识别任务的是名称,不是生产者内存里的函数对象。
这类问题出现在同一模块里定义同名闭包,或把不同类中的同名方法注册成任务。Celery 5.6.2 有这一具体表现;5.6.3 的自动命名与注册实现仍采用相同规则。
比较 name,比比较对象地址有用
没有显式指定 name 时,Celery 默认用函数的模块名和 __name__ 组成任务名,没有把 __qualname__ 纳入其中。工厂调用了两次,内部函数仍可能都叫 scale,于是都注册为 jobs.scale。
例如,两个任务打印出的 .name 相同,worker 日志也都记录这个名字,而预期计算分别是 10 * 2 和 10 * 3,就应沿注册表检查。@app.task 路径遇到已存在的名称会复用注册对象;不能靠调换工厂调用顺序解决,顺序变化只可能让被复用的实现发生变化。
类名不同也不够。ClassA.process 和 ClassB.process 的限定名不同,但如果注册时仍采用同一模块下的 process 作为函数名,自动生成的任务名仍有冲突空间。
记录生产者和 worker 使用的模块、代码版本以及每个任务的 .name。在获准的管理环境中,可查看 worker 已注册的名称:
celery -A jobs inspect registered
这里 jobs 是应用入口示例,应替换成现有 Celery 应用模块。这个列表能发现缺失的预期名称,但同一个键已经覆盖了哪一段实现,还要结合启动代码和日志判断。
在创建任务时就给出不同名称
若业务确实需要两个固定倍率任务,可以把稳定名称作为工厂参数。下面是应用已配置 broker 和结果后端时的定义示例,不是要求在模块导入阶段发送任务:
# jobs.py
from celery import Celery
app = Celery("jobs")
def make_scaler(task_name, factor):
@app.task(name=task_name)
def scale(value):
return value * factor
return scale
double = make_scaler("jobs.scale_double", 2)
triple = make_scaler("jobs.scale_triple", 3)
两个 worker 注册名应分别为 jobs.scale_double 和 jobs.scale_triple。对同一个输入 10,这两段函数定义的预期结果分别为 20、30;在隔离队列中确认执行的 worker、任务名与结果对应后,再接回业务入口。
倍率本来就是每次调用的参数时,也可以只保留一个稳定任务,显式传入 value 和 factor。不要为了绕开命名问题不断生成新任务名,造成生产者能发送、worker 却没有注册对应名称。
已经入队的消息不会随改名自动迁移
改名要同时部署生产者与消费方。生产者先开始发送新名称,而 worker 仍是旧版本,会得到未注册任务错误;只更新 worker,也不会改变队列里旧消息携带的名称。
先保留旧名称、待执行任务清单及发布前后的代码版本,制定旧消息的兼容处理。确认哪一批消息可能已经用错误实现执行,再按业务记录核对结果。SUCCESS 只证明那次注册实现没有抛异常,不能证明它做的是原先想要的运算。
恢复旧代码时同样检查仍在队列中的新名称。不要清空整条队列,或直接重新提交所有成功任务来掩盖结果错误;涉及扣款、通知或数据写入时,补偿和重做应使用原有幂等约束。


