如何根据任务名称限制Celery任务?

时间:2017-05-15 18:05:59

标签: python django celery

我使用Celery从Django应用程序处理异步任务。大多数任务很短,几秒钟就可以运行,但我有一项任务可能需要几个小时。

由于我的服务器上的处理限制,Celery配置为一次只运行2个任务。这意味着如果有人启动了其中两个长期运行的任务,它会有效地阻止所有其他Celery处理站点数小时,这非常糟糕。

有没有办法配置Celery,所以它只处理一种类型的任务,一次不超过一个?类似的东西:

@task(max_running_instances=1)
def my_really_long_task():
    for i in range(1000000000):
        time.sleep(6000)

注意,我不想取消my_really_long_task的所有其他发布。我只是不希望他们立即开始,只有在所有其他同名任务完成后才开始。

由于Celery似乎不支持这一点,我目前的hacky解决方案是查询任务中的其他任务,如果我们找到其他正在运行的实例,则重新安排自己以后运行,例如。

from celery.task.control import inspect

def get_all_active_celery_task_names(ignore_id=None):
    """
    Returns Celery task names for all running tasks.
    """
    i = inspect()
    task_names = defaultdict(int) # {name: count}
    if i:
        active = i.active()
        if active is not None:
            for worker_name, tasks in i.active().iteritems():
                for task in tasks:
                    if ignore_id and task['id'] == ignore_id:
                        continue
                    task_names[task['name']] += 1
    return task_names

@task
def my_really_long_task():

    all_names = get_all_active_celery_task_names()
    if 'my_really_long_task' in all_names:
        my_really_long_task.retry(max_retries=100, countdown=random.randint(10, 300))
        return

    for i in range(1000000000):
        time.sleep(6000)

有更好的方法吗?

我了解其他hacky解决方案,例如this,但设置一个单独的memcache服务器来跟踪任务唯一性的可靠性更低,而且比我上面使用的方法更复杂。

1 个答案:

答案 0 :(得分:2)

另一种解决方案是将my_really_long_task队列到一个单独的队列中。

 my_really_long_task.apply_async(*args, queue='foo')

然后启动并发为1的worker来使用这些任务,这样一次只能执行1个任务。

celery -A foo worker -l info -Q foo