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

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

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

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

我的问题:

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

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

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

谢谢

【问题讨论】:

    标签: jquery python ajax django celery


    【解决方案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 });
    }
    

    任务完成后删除

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2022-01-05
      • 1970-01-01
      • 2015-10-24
      • 1970-01-01
      • 2018-01-07
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多