Django Celery:仅执行长时间运行进程的一个实例

时间:2012-01-17 23:10:12

标签: django concurrency multiprocessing rabbitmq celery

我有一个长时间运行的进程,必须每五分钟运行一次,但多个进程实例不应该同时运行。该过程通常不应该超过五分钟,但我想确保第二个实例在它运行时不会启动。

根据previous recommendation,我正在使用Django Celery来安排这个长期运行的任务。

我不认为定期任务会起作用,因为如果我有五分钟的时间,如果另一个任务实例已经运行,我不希望执行第二个任务。

我目前的实验如下:在8:55,任务的一个实例开始运行。当任务完成时,它将触发另一个自身的实例在接下来的五分钟标记处运行。因此,如果第一个任务在8:57结束,则第二个任务将在9:00运行。如果第一个任务碰巧运行时间很长并且在9:01结束,它将安排下一个实例在9:05运行。

除了下面的简单示例之外,我一直在努力解决各种神秘错误。我还没有找到任何其他人从之前的实例调度任务的例子。我想知道是否有更好的方法来做我想做的事情。我知道有一种方法可以命名一个人的任务;也许有办法搜索具有相同名称的运行或计划实例?有没有人有任何关于每五分钟运行一次任务的建议,但是确保一次只运行一个任务?

谢谢你, 乔

在mymodule / tasks.py中:

import datetime
from celery.decorators import task 

@task 
def test(run_periodically, frequency):

    run_long_process()
    now = datetime.datetime.now()
    # Run this task every x minutes, where x is an integer specified by frequency
    eta = (
      now - datetime.timedelta(
         minutes =  now.minute % frequency , seconds = now.second, 
         microseconds = now.microsecond ) ) + datetime.timedelta(minutes=frequency) 
    task = test.apply_async(args=[run_periodically, frequency,], eta=eta)  

来自./manage.py shell:

from mymodule import tasks
result = tasks.test.apply_async(args=[True, 5])

3 个答案:

答案 0 :(得分:7)

您可以使用与特殊锁定配对的定期任务,以确保一次执行一个任务。以下是Celery文档的示例实现:

http://ask.github.com/celery/cookbook/tasks.html#ensuring-a-task-is-only-executed-one-at-a-time

您描述的上一次执行调度任务的方法可以在其中一项任务失败的情况下停止执行任务。

答案 1 :(得分:0)

我们使用芹菜一次,它为我们解决了类似的问题。 Github链接-https://github.com/cameronmaske/celery-once

它具有非常直观的界面,并且比celery文档中推荐的界面更容易合并。

答案 2 :(得分:0)

我个人通过使用 task.name + args 之类的键缓存标志来解决这个问题

def perevent_run_duplicate(func):
    """
        this decorator set a flag to cache for a task with specifig args
        and wait to completion, if during this task received another call
        with same cache key will ignore to avoid of conflicts.
        and then after task finished will delete the key from cache

        - cache keys with a day of timeout

    """

    @wraps(func)
    def outer(self, *args, **kwargs):
        if cache.get(f"running_task_{self.name}_{args}", False):
            return
        else:
            cache.set(f"running_task_{self.name}_{args}", True, 24 * 60 * 60)
        func(self, *args, **kwargs)
        cache.delete(f"running_task_{self.name}_{args}")

    return outer 

此装饰器将管理任务调用,以防止相同参数对任务的重复调用。