我在Celery + RabbitMQ结果后端用custom states实现了长任务的进度反馈。
但是调用者无法按照我的预期来恢复最新的进展状态。在以下代码中,result.info['step']
始终返回0
,然后任务将以“result = 42”结束。
# tasks.py -- celery worker
from celery import Celery
app = Celery('tasks', backend='amqp', broker='amqp://guest@localhost//')
@app.task
def long_task():
for i in range(0, 10):
timer.sleep(10) # some work
self.update_state(state='PROGRESS', meta={'step': i})
return 42
# caller.py
from tasks import long_task
result = long_task.delay()
while not (result.successful() or result.failed()):
try:
result.get(timeout=1)
except celery.exceptions.TimeoutError:
if result.state == 'PROGRESS':
print("progress={}".format(result.info['step']))
print("result={}".format(result.get()))
Python 3.4.1 / Celery 3.1.17 / RabbitMQ 3.4.4
答案 0 :(得分:4)
我认为这是一个微妙的时间问题,再加上RabbitMQ result backend将任务结果作为消息发送,并且只能检索一次。
预先简短回答:避免在真正需要最终结果之前调用result.get()
:
while not result.ready():
if result.state == "PROGRESS":
print("progress={}".format(result.info['step']))
time.sleep(1)
print("result={}".format(result.get()))
# +additional cleanup: see comments below
更长的答案是,这里有两种方法(和一个属性)与AMQP后端对话:
调用AMQPBackend.wait_for()
,它会消耗任务队列中的所有结果,直到celery.states.READY_STATES
中的状态结果出现。
AsyncResult.successful()
,AsyncResult.failed()
,AsyncResult.info
调用AMQPBackend.get_task_meta()
,它消耗任务队列的所有结果,然后缓存并返回最新的结果。如果未检索到任何消息,则后端将返回缓存结果或PENDING
结果。注意:后端的最新消息是requeued,如果是the final result,它将被AsyncResult
实例 1 缓存。
调用result.get()
会消耗所有状态更新,result.info
无法提供最新的进度报告;相反,它很可能是一个陈旧的缓存,其中一个AsyncResult.get_task_meta()
的调用设法在某个时刻抓住了。
因此,根据时间安排,step
可能会在最差的情况下停留在0,其中最差的是PROGRESS
状态从未到达调用者。
1 由于最终结果在通过调用get_task_meta()
获取时都会重新排队并缓存,因此您需要手动排空队列,如下面的评论中所述。