我正在使用Celery在我正在开发的Django应用程序中处理任务调度,我正在使用Django数据库进行测试。
我只是尝试了一些事情来处理任务的执行,只要它没有按照article中的建议进行安排或进行,但到目前为止没有任何工作。
这样的事情:
task.py
@task()
def add(x, y):
return x + y
然后当你按照以下方式调用它时两次:
import myapp.tasks.add
myapp.tasks.add.apply_async((2,2), task_id=1, countdown=15)
myapp.tasks.add.apply_async((2,2), task_id=2, countdown=15)
它应该允许一个基于countdown=15
的实例。如果有另一个正在运行或等待的话,我怎么能完成第二个调用永远不会执行它?
答案 0 :(得分:8)
接受答案的一个问题是它很慢。检查任务是否已在运行涉及调用代理,然后迭代运行和活动任务。如果你想快速排队任务,这将无法正常工作。此外,当前的解决方案具有较小的竞争条件,因为2个进程可以检查任务是否已排队等同(找出它不是),这将排队2个任务。
更好的解决方案是我称之为去抖动的任务。基本上,每次排队任务时都会增加一个计数器。当任务开始时,你减少它。使用redis然后它就是原子。
e.g。
排队任务:
conn = get_redis()
conn.incr(key)
task.apply_async(args=args, kwargs=kwargs, countdown=countdown)
然后在任务中,您有2个选项,是否要在第一个排队(节流)后15秒执行任务,或者在最后一个排队后15秒执行任务(去抖动)。也就是说,如果我们继续尝试运行相同的任务,我们是否延长了计时器,或者我们只是等待第一个计时器,并忽略排队的其他任务。
很容易支持两者,这里是debounce,我们等到任务停止排队:
conn = get_redis()
counter = conn.decr(key)
if counter > 0:
# task is queued
return
# continue on to rest of task
油门版本:
counter = conn.getset(key, '0')
if counter == '0':
# we already ran so ignore all the tasks that were queued since
return
# continue on to task
此解决方案相对于接受的另一个好处是密钥完全在您的控制之下。因此,如果您希望执行相同的任务,但仅针对不同的ID /对象执行一次,则将其合并到您的密钥中。
更新
考虑到这一点,你可以更轻松地完成油门版本,而无需排队任务。
节气门v2(排队时)
conn = get_redis()
counter = conn.incr(key)
if counter == 1:
# queue up the task only the first time
task.apply_async(args=args, kwargs=kwargs, countdown=countdown)
然后在任务中将计数器设置回0。
你甚至不必使用计数器,如果你有一套你可以将钥匙添加到该套装。如果你回到1,那么密钥就不在集合中,你应该排队任务。如果你回到0,那么密钥已经在集合中,所以不要对任务进行排队。
答案 1 :(得分:2)
在你跳跃之前先看看!在排队任务之前,您可以检查是否有任何正在运行/等待的任务。
from celery.task.control import inspect
def is_running_waiting(task_name):
"""
Check if a task is running or waiting.
"""
scheduled_tasks = inspect().scheduled().values()[0]
for task in scheduled_tasks:
if task['request']['name'] == task_name:
return True
running_tasks = inspect().active().values()[0]
for task in running_tasks:
if task['request']['name'] == task_name:
return True
现在,如果你排队三个添加任务,第一个将排队等待执行,剩下的不会排队。
for i in range(3):
if not is_running_waiting('add'):
add.apply_async((2,2), countdown=15)