簡體   English   中英

Django Celery:根據用戶輸入在運行時按計划創建周期性任務

[英]Django Celery: create periodic task at runtime with schedule depending on user input

我有一個簡單的 Django (v3.1) 應用程序,我從表單接收數據,用視圖處理它,然后將它傳遞給 Celery(v4.4.7,RabbitMQ 作為代理)。 根據表單中提交的數據,它可以是一次性任務或周期性任務。

周期性任務應該執行與一次性任務相同的任務,但是,嗯,有一個周期性的計划。 我想將該計划傳遞給任務,包括開始日期、結束日期和間隔(例如:每 2 天下午 4 點執行一次,從現在開始直到 4 周)。

我的觀點(當然,為了說明目的而縮短和重命名):

# views.py

if request.method == 'POST':
    form = BackupForm(request.POST)
        
    if form.is_valid():
        data = ...

        if not form.cleaned_data['periodic']: 
            # execute one-time task 
            celery_task = single_task.delay(data)

        else:
            schedule = {
                'first_backup': form.cleaned_data['first_backup'],
                'last_backup': form.cleaned_data['last_backup'],
                'intervall_every': form.cleaned_data['intervall_every'],
                'intervall_unit': form.cleaned_data['intervall_unit'],
                'intervall_time': form.cleaned_data['intervall_time'],
            }

            # execute periodic task, depending on the schedule submitted in the form
            celery_task = single_task.delay(data, schedule=schedule)

        return HttpResponseRedirect(reverse('app:index'))

單個任務如下所示:

# tasks.py

@shared_task
def single_task(data: dict, **kwargs) -> None:
    asyncio.run(bulk_screen(data=data))
    # TODO: receive schedule if periodic and create a periodic task with it

這適用於單個任務。 但是,我不知道如何調整它以創建動態周期性任務。 我的日程安排數據因用戶的表單輸入而異。 我必須在運行時創建周期性任務。

根據有關定期任務官方文檔crontab 計划是我需要的:

from celery.schedules import crontab

app.conf.beat_schedule = {
    # Executes every Monday morning at 7:30 a.m.
    'add-every-monday-morning': {
        'task': 'tasks.add',
        'schedule': crontab(hour=7, minute=30, day_of_week=1),
        'args': (16, 16),
    },
}

雖然這看起來不錯,但它位於帶有硬編碼時間表的 celery 配置中。

我還閱讀了on_after_finalize.connect裝飾器,我可以在其中執行以下操作:

@celery_app.on_after_finalize.connect
def setup_periodic_tasks(sender, **kwargs):
    sender.add_periodic_task(10.0, task123.s('hello'))

但我不知道如何將時間表傳遞給這個函數。 另外,發件人是什么? 我可以從我的角度通過它嗎?

然后我閱讀了有關在 celery beat 中填充相關模型的信息 但我想必須有一種更優雅的方式,使用沒有過時裝飾器的穩定版本。

謝謝你。

您絕對應該嘗試填充django_celery_beat包提供的PeriodicTaskCrontabSchedule 鏈接到文檔

Celery beat 是一個定期運行的調度器,它會簡單地根據一個調度執行所有任務(在django_celery_beat情況下,一個數據庫支持一個)。 參考文獻 1 , 參考文獻 2

Celery beat 無疑是處理具有不同計划的周期性任務的最干凈的方式,而不是創建自己的計划程序或處理不同的計划。

我相信在 celery 4.x 中你不能設置你需要的動態時間表(我記得看到它出現在 5 中,但不確定它是否有)。 但是,您的代碼看起來很有希望——您只需要檢查single_task計划並使用countdowneta計划新任務(如果需要)。 任務可以為將來的時間安排它自己的任務

例如

@shared_task
def single_task(data: dict, **kwargs) -> None:
    asyncio.run(bulk_screen(data=data))
    schedule = kwargs.get("schedule")
    if schedule:  # do we need to run this again?
        next_run_at = get_next_run_at(schedule)
        if next_run_at:
            # yes!
            single_task.apply_async(args=[data], kwargs=kwargs, eta=next_run_at)
        # else the task will complete without rescheduling itself

def next_run_at_schedule(schedule: dict) -> Optional[datetime]:
    """ return None if the schedule has expired, or is invalid
        else return the date and time to run the next task
    """
    pass 

有關apply_asynceta詳細信息,請參閱Celery 文檔 我還沒有測試過這個,但原理是合理的。 你應該

  • single_task添加錯誤處理,以便在主代碼中出現錯誤時安排后續運行
  • 可能在執行主要任務之前添加額外的檢查以確保您沒有運行僵屍任務(例如用戶已取消訂閱等)
  • 絕對確保驅動時間表的用戶輸入已經過徹底驗證——不僅僅是日期是日期等。“永遠不要相信用戶輸入”

如果您願意,您可以在 apply_async 中使用countdown而不是eta

當然,將計划加載到數據庫中然后每分鍾運行一個簡單的 celery 任務來檢查需要發生的備份可能更容易(也更可靠,因為正確執行我提到的錯誤處理並非微不足道)然后將通過simple_task.delay(data)並行觸發它們。

暫無
暫無

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

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