RuntimeError:永远不要在任务 Celery 中调用 result.get()

Posted

技术标签:

【中文标题】RuntimeError:永远不要在任务 Celery 中调用 result.get()【英文标题】:RuntimeError: Never call result.get() within a task Celery 【发布时间】:2018-01-11 10:38:10 【问题描述】:

我正在使用 celery 向远程服务器发送任务并尝试取回结果。在远程服务器上使用update_state 方法不断更新任务状态。

我正在使用

发送任务
app.send_task('task_name')

获取 celery 任务的结果是一个阻塞调用,我不希望我的 django 应用等待结果和超时。

所以我尝试运行另一个 celery 任务以获得结果。

@app.task(ignore_result=True)
def catpure_res(task_id):
    task_obj = AsyncResult(task_id)
    task_obj.get(on_message=on_msg)

但是会导致下面的错误。

Traceback (most recent call last):
  File "/usr/local/lib/python2.7/dist-packages/celery/app/trace.py", line 367, in trace_task
    R = retval = fun(*args, **kwargs)
  File "/usr/local/lib/python2.7/dist-packages/celery/app/trace.py", line 622, in __protected_call__
    return self.run(*args, **kwargs)
  File "/home/arpit/project/appname/tasks/results.py", line 42, in catpure_res
    task_obj.get(on_message=on_msg)
  File "/usr/local/lib/python2.7/dist-packages/celery/result.py", line 168, in get
    assert_will_not_block()
  File "/usr/local/lib/python2.7/dist-packages/celery/result.py", line 44, in assert_will_not_block
    raise RuntimeError(E_WOULDBLOCK)
RuntimeError: Never call result.get() within a task!
See http://docs.celeryq.org/en/latest/userguide/tasks.html#task-synchronous-subtasks

此错误是否有任何解决方法。我是否必须运行守护进程才能获得结果?

【问题讨论】:

【参考方案1】:

使用allow_join_result。请参阅下面的 sn-p。

@app.task(ignore_result=True)
def catpure_res(task_id):
    task_obj = AsyncResult(task_id)
    with allow_join_result():
        task_obj.get(on_message=on_msg)

注意:正如其他答案中提到的那样,它可能会导致性能问题甚至死锁,但如果您的任务写得很好并且不会导致意外错误,那么它应该会像魅力一样工作。

【讨论】:

正是我想要的。谢谢。 “task_obj.get(on_message=on_msg)”中的“on_msg”从何而来?【参考方案2】:

正如您的标题所解释的,在任务中调用get 是一种不好的做法,可能会导致死锁。 相反,您可以检查任务状态和get 它在准备就绪时的结果:

result = catpure_res.AsyncResult(task_id, app=app)
    if result.ready():
        return result.get()

    return result.state

您可以将上述 sn-p 包装在一个函数中,并每隔 x 秒请求一次。

编辑:尊重您的评论:

您可以改为获取result.state,并将retry 机制与countdown 一起使用,直到任务result.state == SUCCESS

您可以添加 celery beat 来运行定期任务,检查主要任务是否结束。

请注意,使用如此繁重的任务(持续时间长)也是一种不好的做法。考虑将其分解为小任务,并使用canvas 将它们组合起来。

【讨论】:

每秒 ping 一次会消耗计算成本。此外,该任务非常繁重,可以运行数天,它会更新其状态,说明哪个步骤已完成以及它的元信息是什么。尽管您的建议很好,但在我的情况下似乎不起作用 运行节拍,一切似乎都是一个大设置。它也是一个单一的任务,它不能被分解。你可以把它想象成一个单一的功能。看上面的答案 我知道 Celery 说这是不好的做法,但我完全不同意。任务不应与子任务不同。我有可能被自己调用或作为子任务调用的脚本和一些需要运行子任务的子任务,如果我遵循这些指南,我的设计会复杂得多。

以上是关于RuntimeError:永远不要在任务 Celery 中调用 result.get()的主要内容,如果未能解决你的问题,请参考以下文章

永远不要使用Redis过期监听实现定时任务!

Discord.py 带线程,RuntimeError: Timeout context manager 应该在任务内部使用

游戏工程师成长 积极主动

RuntimeError:事件循环已关闭任务被破坏但它正在等待 Discord Python

Celery第一步

celery基础