Python 报告产生了长期运行芹菜任务的结果 问题
我已经将一个长期运行的任务分割为逻辑子任务,这样我就可以在每个子任务完成时报告其结果。然而,我正在尝试报告一项实际上永远不会完成的任务的结果(而是在执行过程中产生价值),并且我正在用现有的解决方案努力做到这一点 背景 我正在为我编写的一些Python程序构建一个web界面。用户可以通过web表单提交作业,然后查看作业的进度 假设我有两个函数,每个函数通过单独的表单访问:Python 报告产生了长期运行芹菜任务的结果 问题,python,django,celery,Python,Django,Celery,我已经将一个长期运行的任务分割为逻辑子任务,这样我就可以在每个子任务完成时报告其结果。然而,我正在尝试报告一项实际上永远不会完成的任务的结果(而是在执行过程中产生价值),并且我正在用现有的解决方案努力做到这一点 背景 我正在为我编写的一些Python程序构建一个web界面。用户可以通过web表单提交作业,然后查看作业的进度 假设我有两个函数,每个函数通过单独的表单访问: med_func:执行大约需要1分钟,结果会传递到render(),从而生成附加数据 long\u func:返回生成器。每
:执行大约需要1分钟,结果会传递到med_func
,从而生成附加数据render()
:返回生成器。每个long\u func
约为30分钟,应向用户报告。有这么多的收益率,我们可以把这个迭代器看作是无限的(仅当终止时)。< /LI>产量
med_func
,我报告如下结果:
在提交表单时,我将AsyncResult
保存到:
结果页面的Django视图访问此AsyncResult
。任务完成后,结果保存到对象中,该对象作为上下文传递给Django模板
def results(request):
""" Serve (possibly incomplete) results of a session's latest run. """
session = request.session
try: # Load most recent task
task_result = session["task_result"]
except KeyError: # Already cleared, or doesn't exist
if "results" not in session:
session["status"] = "No job submitted"
else: # Extract data from Asynchronous Tasks
session["status"] = task_result.status
if task_result.ready():
session["results"] = task_result.get()
render_task = task_result.children[0]
# Decorate with rendering results
session["render_status"] = render_task.status
if render_task.ready():
session["results"].render_output = render_task.get()
del(request.session["task_result"]) # Don't need any more
return render_to_response('results.html', request.session)
此解决方案仅在函数实际终止时有效。我无法将long_func
的逻辑子任务链接在一起,因为有数量未知的yield
s(long_func
循环的每次迭代可能不会产生结果)
问题:
是否有任何合理的方法可以访问运行时间非常长的芹菜任务中生成的对象,以便在生成器耗尽之前显示它们?芹菜部分:
def long_func(*args, **kwargs):
i = 0
while True:
yield i
do_something_here(*args, **kwargs)
i += 1
@task()
def test_yield_task(task_id=None, **kwargs):
the_progress = 0
for the_progress in long_func(**kwargs):
cache.set('celery-task-%s' % task_id, the_progress)
Webclient端,启动任务:
r = test_yield_task.apply_async()
request.session['task_id'] = r.task_id
测试最后产生的价值:
v = cache.get('celery-task-%s' % session.get('task_id'))
if v:
do_someting()
如果您不喜欢使用缓存,或者不可能使用缓存,您可以使用数据库、文件或芹菜工人和服务器端都可以访问的任何其他位置。使用缓存是一种最简单的解决方案,但工作人员和服务器必须使用相同的缓存。需要考虑以下几个选项: 1——任务组。如果可以枚举调用时的所有子任务,则可以将组作为一个整体应用,即返回一个TaskSetResult对象,可用于监视整个组或组中单个任务的结果,并在需要检查状态时根据需要进行查询 2--回调。如果不能枚举所有子任务(或者即使可以!),则可以定义一个web钩子/回调,这是任务的最后一步——在任务的其余部分完成时调用。钩子将针对应用程序中的URI,该URI接收结果并通过DB或应用程序内部API使其可用
这些组合可以解决您的难题。为了让芹菜知道任务的当前状态,它会在您拥有的任何结果后端中设置一些元数据。您可以利用它来存储其他类型的元数据
def yielder():
for i in range(2**100):
yield i
@task
def report_progress():
for progress in yielder():
# set current progress on the task
report_progress.backend.mark_as_started(
report_progress.request.id,
progress=progress)
def view_function(request):
task_id = request.session['task_id']
task = AsyncResult(task_id)
progress = task.info['progress']
# do something with your current progress
我不想在这里输入太多数据,但它可以很好地跟踪长期运行任务的进度。另请参见Instagram工程师之一的伟大PyCon preso
在视频mark 16:00中,他讨论了他们如何构建长长的子任务列表。保罗的答案很好。作为使用
mark\u As\u start
的替代方法,您可以使用Task
的update\u state
方法。它们最终会做同样的事情,但是“update_state”这个名称更适合您尝试做的事情。您可以选择定义一个指示任务正在进行的状态(我已将自定义状态命名为“进度”):
这个问题很模糊,评论提出了一个完全不同的问题。您可以详细说明实际问题“是否有任何合理的方法来渲染运行时间非常长的芹菜任务中生成的对象?”?使用“渲染”是指您希望以某种方式获得视图函数中未结束任务的结果?这些结果看起来如何(模型实例?)?公平点,@Brent和@Bernhard;这里我要说的不是实际呈现结果本身,而是能够在生成器完成之前访问生成的值(以便可以在页面上呈现)。因此,是的,我本质上是试图在任务终止之前,在视图函数中尽可能多地显示任务的进度。而@Bernhard,“结果”只是标准的Python对象,完全独立于芹菜和Django(我可以将该程序用作独立的Python应用程序)。我得到以下错误:
Cellery.backends.amqp.backloglimited:483bb075-a27c-4298-8227-3BBEB13074F
。有解决办法吗?
def yielder():
for i in range(2**100):
yield i
@task
def report_progress():
for progress in yielder():
# set current progress on the task
report_progress.backend.mark_as_started(
report_progress.request.id,
progress=progress)
def view_function(request):
task_id = request.session['task_id']
task = AsyncResult(task_id)
progress = task.info['progress']
# do something with your current progress
def yielder():
for i in range(2**100):
yield i
@task
def report_progress():
for progress in yielder():
# set current progress on the task
report_progress.update_state(state='PROGRESS', meta={'progress': progress})
def view_function(request):
task_id = request.session['task_id']
task = AsyncResult(task_id)
progress = task.info['progress']
# do something with your current progress