我使用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服务器来跟踪任务唯一性的可靠性更低,而且比我上面使用的方法更复杂。
答案 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