簡體   English   中英

如果尚未使用celery安排任務,則允許執行任務

[英]Allow a task execution if it's not already scheduled using celery

我正在使用Celery來處理我正在開發的Django應用程序中的任務調度,我正在使用Django數據庫進行測試。

我只是想幾件事情要處理,只有當它不是已經計划任務的執行,或者在這樣的提議進步文章 ,到目前為止,但沒有工作。

像這樣的東西:

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實例。 如果有另一個正在運行或等待的話,我怎么能完成第二個調用從不執行它?

接受的答案的一個問題是它很慢。 檢查任務是否已在運行涉及調用代理,然后迭代運行和活動任務。 如果您想快速排隊任務,這將無法正常工作。 此外,當前解決方案具有較小的競爭條件,因為2個進程可以檢查任務是否已經排隊等同(發現它不是),這將排隊2個任務。

更好的解決方案是我稱之為去抖動的任務。 基本上,每次排隊任務時都會增加一個計數器。 當任務開始時,你減少它。 使用redis然后它都是原子的。

例如

排隊任務:

conn = get_redis()
conn.incr(key)
task.apply_async(args=args, kwargs=kwargs, countdown=countdown)

然后在任務中,您有2個選項,是否要在第一個排隊(節流)后15秒執行任務,或者在最后一個排隊后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,則密鑰已經在集合中,因此不要對任務進行排隊。

三思而后行! 在排隊任務之前,您可以檢查是否有任何正在運行/等待的任務。

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)

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM