任务进度未更新Celery + RabbitMQ的最新状态

时间:2015-03-05 05:29:11

标签: python rabbitmq celery

我在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

1 个答案:

答案 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后端对话:

  • AsyncResult.get()

    调用AMQPBackend.wait_for(),它会消耗任务队列中的所有结果,直到celery.states.READY_STATES中的状态结果出现。

  • AsyncResult.successful()AsyncResult.failed()AsyncResult.info

    调用AMQPBackend.get_task_meta(),它消耗任务队列的所有结果,然后缓存并返回最新的结果。如果未检索到任何消息,则后端将返回缓存结果或PENDING结果。注意:后端的最新消息是requeued,如果是the final result,它将被AsyncResult实例 1 缓存。

    < / LI>

调用result.get()会消耗所有状态更新,result.info无法提供最新的进度报告;相反,它很可能是一个陈旧的缓存,其中一个AsyncResult.get_task_meta()的调用设法在某个时刻抓住了。

因此,根据时间安排,step可能会在最差的情况下停留在0,其中最差的是PROGRESS状态从未到达调用者。

1 由于最终结果在通过调用get_task_meta()获取时都会重新排队并缓存,因此您需要手动排空队列,如下面的评论中所述。