中文字幕av专区_日韩电影在线播放_精品国产精品久久一区免费式_av在线免费观看网站

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

詳解Django定時任務模塊設計與實踐

發布時間:2020-09-20 06:32:43 來源:腳本之家 閱讀:235 作者:嚴北 欄目:開發技術

在開發后臺與任務相關的功能中,遇到一個需求:用戶需要能夠為任務配置定時策略,使任務定時執行某個操作。

需求分析

根據需求,我們可以拆解成如下幾個步驟:

  • 「某個操作」的實現
  • 配置為定時任務
  • 定時策略可配置
  • 用戶體驗好

其中步驟 1 與本文無關不提;對于定時任務的實現,在上節Celery異步任務隊列 有簡單提到 celery 也支持定時任務。

Celery 的定時任務策略配置于代碼中,在啟動 celery 時寫入本地shelve 文件,不利于管理。

因此在 celery 的文檔中也提到一個擴展模塊 django-celery-beat ,該模塊將定時任務的配置寫入 Django 配置的數據庫中,當程序啟動后可以通過 admin 后臺進行管理,并且可以直接通過 ORM 對定時任務配置進行修改,無需修改代碼然后重啟 celery,符合我們預期。

當然還有很多其他庫也能實現,因為我們已經使用 celery 執行異步任務,所以本文還是用 django-celery-beat 解決問題。

Celery 的定時任務使用的是類似 crontab 的語法,因此在用戶體驗上,要考慮普通用戶的學習成本,可以提供一些常用的配置,例如每周的工作日每天 1 點執行任務;也要考慮后期的擴展性,可以提供輸入框方便配置。

設計與實現

基本用法

定時策略(CrontabSchedule)

CrontabSchedule 支持類 crontab 語法,同樣是 5 個配置域,分別為:

  • 每周中的天
  • 每月中的天
  • 每年中的月

每個配置域使用空格隔開。

對每個配置域常用語法:

  • * : 范圍內的所有值
  • M-N : M到N之間的值
  • M-N/X*/X : 每X分鐘、每X天等等
  • A,B,...,Z : 枚舉的值

舉個例子: 每個工作日1點執行: 0 1 1-5 * *

創建定時策略代碼如下:

from django_celery_beat.models import CrontabSchedule, PeriodicTask
>>> schedule, _ = CrontabSchedule.objects.get_or_create(
... minute='30',
... hour='*',
... day_of_week='*',
... day_of_month='*',
... month_of_year='*',
... )

定時任務

定時任務可以依賴不同的定時策略,例如 crontab, interval 等,創建時指定 schedule 即可。以 crontab 定時任務為例:

>>> import json
>>> from datetime import datetime, timedelta

>>> PeriodicTask.objects.create(
... crontab=schedule,   # we created this above.
... name='Importing contacts',  # simply describes this periodic task.
... task='proj.tasks.import_contacts', # name of task.
... args=json.dumps(['arg1', 'arg2']),
... kwargs=json.dumps({
... 'be_careful': True,
... }),
... expires=datetime.utcnow() + timedelta(seconds=30)
... )

其中 name 為定時任務的名稱,每個任務名必須唯一; task 為需要執行的 celery 任務。加上定時策略調度器,這三個是一個定時任務所必須的屬性。

定時任務還有其他配置,如 args / kwargs 對應一個 celery 任務的入參; expires 設置了該定時任務的過期時間。

Django配置

最基礎的配置只需要在 INSTALLED_APPS 中添加引用,并設置定時任務調度器即可:

settings.py

INSTALLED_APPS = [
 ...
 'django_celery_beat'
]

# 配置 celery 定時任務使用的調度器
CELERY_BEAT_SCHEDULER = 'django_celery_beat.schedulers:DatabaseScheduler'

時區問題

在使用 django-celery-beat 過程中遇到兩個關于時區的問題:

創建的定時任務,實際觸發時間與配置的時間存在8小時時間差

解決方案:

8小時明顯是因為時區不同導致,而 django-celery-beat 對時區的處理似乎總有問題(若不對請指教)。

修改 settings.py 中的時區配置:

settings.py

# 設置 Django 大部分應用通用的時區
TIME_ZONE = 'Asia/Shanghai'
# 關閉 UTC
USE_TZ = False
CELERY_ENABLE_UTC = False
# 設置 django-celery-beat 真正使用的時區
CELERY_TIMEZONE = TIME_ZONE
# 使用 timezone naive 模式
DJANGO_CELERY_BEAT_TZ_AWARE = False

關于 timezone naive 與 timezone aware 模式的區別可以參考文章:Django時區詳解

簡單來說就是,naive 模式不存儲時區信息,只存儲經過時區轉換后的時間;反之 aware 模式則存儲了 UTC 時間和 UTC 時區信息。

根據文檔,在修改了時區后,需要將已執行過的定時任務的 last_run_at 重置為 None

python manage.py shell
>>> from django_celery_beat.models import PeriodicTask
>>> PeriodicTask.objects.all().update(last_run_at=None)

修改完成后,重啟 celery beat

PS: 就算是經過這樣配置,我也仍然遇到了任務不斷執行的問題,并且在我多次重啟 celery 后不再復現,因此本配置可能還有問題。

數據庫中, CrontabScheduletimezone 配置始終是 UTC

解決方案:

查看 CrontabSchedule 模型的源碼,找到數據庫中 timezone 字段的屬性:

class CrontabSchedule(models.Model):
 ...
 timezone = timezone_field.TimeZoneField(
 default='UTC',
 verbose_name=_('Cron Timezone'),
 help_text=_(
  'Timezone to Run the Cron Schedule on. Default is UTC.'),
 )

由于我們在創建 CrontabSchedule 實例時并未指定 timezone ,因此在創建任務時,添加該字段的配置即可:

from django_celery_beat.models import CrontabSchedule
>>> schedule, _ = CrontabSchedule.objects.get_or_create(
... minute='30',
... hour='*',
... day_of_week='*',
... day_of_month='*',
... month_of_year='*',
... timezone='Asia/Shanghai'
... )

*業務前后端設計

本節內容僅供參考,不一定適用其他場景。

前端

設計前端定時任務配置項,包含一個開關,一個三選一單選組件,以及一個輸入框:

詳解Django定時任務模塊設計與實踐

為了方便非技術人員設置定時任務,優化用戶體驗,定時任務除了「自定義」的輸入模式,還有一個「每天」與「每周」的選項:

  • 每天:0 1 1-5 * *
  • 每周:0 1 1 * *

單選框與字符串雙向綁定,在后端返回上面兩個字符串之一時選中每天或每周,否則選中自定義選項。

后端

假設對于我的業務來說,前端需要的任務數據字段為:

{
 "task_id": 1,
 "is_periodic_task": true,
 "periodic_task_id": 1,
 "crontab": "* * * * *"
}

ER 模型如圖:

詳解Django定時任務模塊設計與實踐

返回給前端的數據中,若 periodic_task 不為空,則 is_periodic_taskTrue ,并通過 periodic_task.crontab_id 獲取到 CrontabSchedule 實例,轉化為字符串返回。

要注意, CrontabSchedule__str__ 方法除了返回 crontab 配置,還會返回時區等信息,而這些信息前端展示時并不需要。

因此可以新建一個方法:

def get_crontab_str(contab) -> str:
 """
 獲取前端配置需要的 5 項值
 :param contab: CrontabSchedule對象
 :return:
 """
 return '{0} {1} {2} {3} {4}'.format(
 cronexp(contab.minute), cronexp(contab.hour),
 cronexp(contab.day_of_week), cronexp(contab.day_of_month),
 cronexp(contab.month_of_year)
 )

序列化時調用該方法返回給前端即可。

修改任務

修改任務包括以下三種情況

  • 從定時任務改為非定時任務
  • 從非定時任務改為定時任務
  • 在定時任務基礎上修改定時策略

對應流程圖如下:

1:

詳解Django定時任務模塊設計與實踐

2, 3:

詳解Django定時任務模塊設計與實踐

圖中「修改配置中的」指前端傳來的修改請求中的新配置信息

具體代碼就不贅述,只提一下暫停定時任務的方法:

修改 PeriodicTask.objects.enabledFalse/0 即可

>>> periodic_task.enabled = False
>>> periodic_task.save()

版本說明

詳解Django定時任務模塊設計與實踐

參考

http://docs.celeryproject.org/en/latest/userguide/periodic-tasks.html
https://django-celery-beat.readthedocs.io/en/latest/
https://docs.djangoproject.com/en/2.2/topics/i18n/timezones/
https://www.jb51.net/article/166085.htm

以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支持億速云。

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

广河县| 湘潭县| 台前县| 新源县| 伽师县| 和平县| 嫩江县| 玛多县| 大埔县| 定兴县| 花垣县| 威宁| 金华市| 晴隆县| 沧源| 天津市| 萍乡市| 甘洛县| 剑川县| 武邑县| 汉沽区| 土默特右旗| 驻马店市| 巫山县| 广汉市| 射阳县| 大庆市| 镇赉县| 巢湖市| 客服| 正宁县| 大英县| 武邑县| 万源市| 二连浩特市| 汶上县| 麦盖提县| 清镇市| 清水河县| 得荣县| 盖州市|