如何判断任务是否已在django-芹菜中排队?

时间:2011-05-04 19:16:44

标签: django message-queue celery django-celery

这是我的设置:

  • django 1.3
  • 芹菜2.2.6
  • django-celery 2.2.4
  • djkombu 0.9.2

在我的settings.py文件中,我有

BROKER_BACKEND = "djkombu.transport.DatabaseTransport"

即。我只是使用数据库来排队任务。

现在我的问题:我有一个用户启动的任务可能需要几分钟才能完成。我希望每个用户只运行一次任务,我会将任务的结果缓存在一个临时文件中,这样如果用户再次启动任务,我只需返回缓存的文件。我的视图函数中的代码看起来像这样:

task_id = "long-task-%d" % user_id
result = tasks.some_long_task.AsyncResult(task_id)

if result.state == celery.states.PENDING:
    # The next line makes a duplicate task if the user rapidly refreshes the page
    tasks.some_long_task.apply_async(task_id=task_id)
    return HttpResponse("Task started...")
elif result.state == celery.states.STARTED:
    return HttpResponse("Task is still running, please wait...")
elif result.state == celery.states.SUCCESS:
    if cached_file_still_exists():
        return get_cached_file()
    else:
        result.forget()
        tasks.some_long_task.apply_async(task_id=task_id)
        return HttpResponse("Task started...")

此代码几乎可以使用。但是当用户快速重新加载页面时,我遇到了问题。在任务排队和最终将任务从队列中拉出并提供给工作人员之间有1-3秒的延迟。在此期间,任务的状态仍为PENDING,导致视图逻辑启动重复任务。

我需要的是一些判断任务是否已经提交到队列的方法,所以我最终不会提交两次。在芹菜中有这样做的标准方法吗?

3 个答案:

答案 0 :(得分:4)

我用Redis解决了这个问题。只需在每个任务的redis中设置一个键,然后在task的after_return方法中从redis中删除该键。 Redis重量轻,速度快。

答案 1 :(得分:4)

我不认为(正如Tomek和其他人所建议的那样)使用数据库是实现此锁定的方法。 django有内置的缓存框架,它应该足以完成这个锁定,并且必须更快。见:

http://docs.celeryproject.org/en/latest/tutorials/task-cookbook.html#cookbook-task-serial

Django可以配置为使用memcached作为缓存后端,这可以分布在多台机器上......这对我来说似乎更好。想法?

答案 2 :(得分:1)

您可以通过手动将结果存储在数据库中来作弊。让我解释一下这将有何帮助。

例如,如果使用RDBMS(带有列的表 - task_id,state,result):

查看部分:

  1. 使用交易管理。
  2. 使用SELECT FOR UPDATE获取task_id ==“long-task-%d”%user_id的行。 SELECT FOR UPDATE将阻止其他请求,直到这一个COMMIT或ROLLBACK。
  3. 如果它不存在 - 将状态设置为PENDING并启动'some_long_task',则结束请求。
  4. 如果状态为待处理 - 通知用户。
  5. 如果状态为SUCCESS - 将状态设置为PENDING,则启动任务,返回'result'列指向的文件。我基于这样的假设,即你想要在获得结果时重新运行任务。 COMMIT
  6. 如果状态为ERROR - 将状态设置为PENDING,则启动任务,通知用户。 COMMIT
  7. 任务部分:

    1. 准备文件,包装在try,catch块中。
    2. 成功时 - 使用state = SUCCESS,result。更新正确的行。
    3. 失败时 - 使用state = ERROR UPDATE正确的行。