仅在单击按钮时运行 celery 任务

Posted

技术标签:

【中文标题】仅在单击按钮时运行 celery 任务【英文标题】:run celery task only once on button click 【发布时间】:2018-04-14 23:55:16 【问题描述】:

我创建了一个带有 celery 任务的 django 应用程序,用于组织数据库表。 celery 任务大约需要 8 秒来完成这项工作(相当繁重的表移动和超过 100 万行的查找)

这个 celery 是通过一个注册用户点击一个按钮开始的。

我在 views.py 中创建了一个任务视图,并在 urls.py 中创建了一个单独的任务 URL。导航到此任务 URL 时,任务启动。

我的问题:

    当您在任务未完成的情况下刷新任务 URL 时,您会启动一个新任务。 使用此设置,您可以导航到另一个页面或重新加载视图

如何防止任务在每次刷新时启动并防止导航到另一个页面。理想情况下,用户只能运行此特定类型的一项任务。

这对 JQuery/Ajax 是否可行?由于我不是专家,也没有使用 JQuery/Ajax 的经验,因此能否有英雄指出我正确的方向。

谢谢

【问题讨论】:

【参考方案1】:

“如何防止任务在每次刷新时启动”

首先确保您使用“POST/redirect/GET”模式:您的视图应该只在“POST”请求时触发任务(GET 请求必须没有副作用),并且然后返回一个HttpResponseRedirect

这不会阻止用户在第一个任务仍在运行时触发第二个任务,但它确实阻止每次刷新(“GET”)页面时重新提交表单。 ..

理想情况下,用户只能运行此特定类型的一项任务。

触发任务时,将其 ID 存储在会话中,在触发任务之前检查会话中的任务 ID。如果有,用它来检查任务的状态。如果完成(无论结果如何),删除任务 ID 并继续执行新任务,否则只显示一条消息,告诉您的用户他已经有一个任务正在运行。

只需确保您正确处理会话中“幽灵”task_id 的情况(它可能是 celery 已经丢弃结果的长期完成任务,或者在工作人员或代理崩溃中丢失的任务 - 是的, sh!t 发生 - 等等)。一个可行的解决方案是实际存储 (timestamp, task_id) 对,如果 celery 没有 task_id 的状态 - 实际上“无状态”被(错误)命名为celery.states.PENDING,这实际上意味着“我不知道这个任务的状态 ATM” - 检查时间戳。如果它比它应该的旧得多,那么你可能会认为它已经死了并且在 6 英尺以下。

并防止导航到另一个页面。

实际上,您为什么要阻止您的用户在此期间执行其他操作?从 UI/UX 的角度来看,一旦任务被触发,您的用户应该被重定向到带有(尽可能)一些进度条或类似反馈的“请稍候”页面。这里的简单(如果有点重)解决方案是使用 ajax 进行一些轮询:您设置一个带有 task_id 的视图,检查结果/进度并将它们作为 json 返回,“请稍候”页面调用此视图(使用 ajax ) 每 X 秒更新一次(并可能在任务完成后将用户重定向到下一页)。

现在,如果有一些操作(除了重新启动相同的任务)您的用户在任务运行时无法执行,您可以对这些操作使用相同的“检查当前任务的会话”机制(使视图装饰器真的很有帮助)。

【讨论】:

嗨 Bruno,我终于使用 Ajax POST 来获取注册到 views.py 的按钮并使用 30 秒超时进行缓存。为一个特定的会话变量设置特定的超时是行不通的。关于导航到另一个页面:这仅在我使用特定 url 指向类似 html 时才相关。 我的缓存目前正在发生的另一件事。我无法从我的 celery 任务中清除缓存,因为这是来自“worker”的缓存,而不是来自“web”进程的缓存。关于如何做到这一点的任何想法?还是去找别的东西然后缓存? 缓存是可以的,直到你必须使它们失效。更严重的是,如果您要使用基于缓存的解决方案,则必须让 Django 和 Celery 进程都可以访问此缓存 - IOW 您必须使用分布式缓存。如果您使用 Redis 作为结果后端,那么也将它用于缓存。如果没有详细信息,就无法提供更多帮助...【参考方案2】:

我相信在服务器端使用锁可以更轻松地完成,请参阅这个问题Running "unique" tasks with celery

如果需要在客户端完成,可能的方法是像这样设置cookie

if (typeof $.cookie("running") === "undefined")
  var date = new Date();
  var minutes = 20;
  date.setTime(date.getTime() + (minutes * 60 * 1000));
  $.cookie("running", "true",  expires: date );

任务完成后删除

【讨论】:

以上是关于仅在单击按钮时运行 celery 任务的主要内容,如果未能解决你的问题,请参考以下文章

安排 Django Celery 定期任务从某个时间运行到另一个时间

如何从 django 模板暂停和停止 celery 任务

如果结果已经可用,则仅在单击按钮时启动异步任务并执行操作,否则等待结果然后执行操作

创建 celery 任务然后同步运行

如何调试在 Eclipse 中本地运行的 Celery/Django 任务

如何在任务中获取芹菜结果模型(使用 django-celery-results)