1、Celery加入異步和定時任務
Celery除了可以異步執行任務之外,還可以定時執行任務。在實例代碼的基礎上寫個測試方法:
import datetime
import timefrom celery import shared_task
from celery.schedules import crontab
from celery.task import periodic_taskfrom FruitGP2.utils import send_active_email@shared_task
def send_activate_email_async(username,to_email):send_active_email(username,to_email)@periodic_task(run_every=datetime.timedelta(hours=1, minutes=15, seconds=40))
@periodic_task(run_every=10)
def some_task():print('periodic task test!!!!!')# celery -A FruitShopGP2 beat -l info
# celery -A FruitShopGP2 worker -l info# @periodic_task(run_every=crontab())
# def some_task():
# print('periodic task test!!!!!')
# print('success')
settings.py
# Celery settingsCELERY_BROKER_URL = 'redis://:@localhost:6379/0'#: Only add pickle to this list if your broker is secured
#: from unwanted access (see userguide/security.html)
CELERY_ACCEPT_CONTENT = ['json']
# CELERY_RESULT_BACKEND = 'db+sqlite:///results.sqlite'
CELERY_RESULT_BACKEND = 'django-db'
CELERY_TASK_SERIALIZER = 'json'
celery.py
from __future__ import absolute_import, unicode_literalsimport osfrom celery import Celery# set the default Django settings module for the 'celery' program.
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'FruitShopGP2.settings')app = Celery('FruitShopGP2')# Using a string here means the worker doesn't have to serialize
# the configuration object to child processes.
# - namespace='CELERY' means all celery-related configuration keys
# should have a `CELERY_` prefix.
app.config_from_object('django.conf:settings', namespace='CELERY')# Load task modules from all registered Django app configs.
app.autodiscover_tasks()@app.task(bind=True)
def debug_task(self):print('Request: {0!r}'.format(self.request))
?
該代碼是每分鐘執行一次some_task方法。
啟動celery是使用worker。但worker不能啟動定時任務。啟動方式如下:
1)先確保中間人是否打開,我使用redis作為中間人。可參考
http://docs.celeryproject.org/en/master/getting-started/first-steps-with-celery.html#redis
https://github.com/celery/celery/blob/master/examples/django/proj/settings.py
2)進入django項目的根目錄執行如下代碼啟動celery的worker:
celery -A FruitShopGP2 worker -l info
其中,myproject是我的django項目名稱。
當出現celery@xxx ready說明redis啟動成功。
有兩個任務,其中一個myapp.tasks.sendmail添加的異步處理任務。myapp.tasks.some_task是本次的定時任務。
3)同樣在django項目的根目錄下再打開一個命令行界面,執行如下代碼:
? ?celery -A FruitShopGP2? beat -l info
可以稍微等待幾分鐘,多執行幾次任務。如下圖 上面的窗口是beat,下面的窗口是worker。可看到print的信息在worker中輸出。
?
?
我們甚至不用打開Django項目的服務器,就可以執行定時任務。?
下方內容僅供參考:
?
2、Celery定時任務時間設置
若你覺得1分鐘等待時間太長。可以設置為每10秒執行一次定時任務。將上面的periodic_task修改如下:
@periodic_task(run_every=10)
修改代碼,需要重啟Celery的worker和beat。
這個run_every參數是設置定時任務的時間間隔或執行的時間。該參數設置有3種方式。
1)直接設置秒數
例如剛剛所說的10秒間隔,run_every=10,每10秒執行一次任務。1分鐘即是60秒;1小時即是3600秒。
2)通過datetime設置時間間隔
有時直接設置秒數不方便,需要通過計算得到具體秒數。
例如,1小時15分鐘40秒 = 1*60*60 + 15*60 + 40。這種情況可讀性也不高。
可以采用datetime設置,代碼如下:
1 from celery.decorators import periodic_task
2 import datetime
3
4 @periodic_task(run_every=datetime.timedelta(hours=1, minutes=15, seconds=40))
5 def some_task():
6 print('periodic task test!!!!!')
代碼可讀性明顯提升,而且設置方便。
但這種不能滿足定時定點的時間設置。假如我想固定每天12點15分的時候,執行一次任務。datetime和直接設置秒數的方式都無法實現。這時得使用第3種方式。
?
3)celery的crontab表達式
crontab是比較完善,且稍微有點復雜(相對前面兩種方式而言)的方式。可以實現我們各種設置時間的需求。
例如,前面給出的代碼:
?
1 #coding:utf-8
2 from celery.task.schedules import crontab
3 from celery.decorators import periodic_task
4
5 @periodic_task(run_every=crontab())
6 def some_task():
7 print('periodic task test!!!!!')?
?
表示每分鐘0秒時刻執行一次(后面不提這個0秒,大家都知道就行了,省點口水)。
其中,crontab()實例化的時候沒設置任何參數,都是使用默認值。crontab一共有7個參數,常用有5個參數分別為:
minute:分鐘,范圍0-59;
hour:小時,范圍0-23;
day_of_week:星期幾,范圍0-6。以星期天為開始,即0為星期天。這個星期幾還可以使用英文縮寫表示,例如“sun”表示星期天;
day_of_month:每月第幾號,范圍1-31;
month_of_year:月份,范圍1-12。
?
a、默認參數
這些參數可以設置表達式,表達稍微復雜的設置。默認值都是"*"星號,代表任意時刻。即crontab()相當與:
1 crontab(minute='*', hour='*', day_of_week='*', day_of_month='*', month_of_year='*')
含義是每天、每小時、每分鐘執行一次任務。這說法太反人類語言習慣,簡單說就是每1分鐘執行一次任務。
?
b、具體某個值
上面提到這些參數的取值范圍。我們可以直接設置某個值。例如:
1 crontab(minute=15)
即每小時的15分時刻執行一次任務。直接指定某個時刻。以此類推可以設置每天0點0分時刻執行任務的設置如下:
1 crontab(minute=0, hour=0)
當然,也可以設置多個值。例如0分和30分執行一次任務:
crontab(minute='0,30')
這里使用字符串,用逗號隔開數值。這里的逗號是表示多個表達式or邏輯關系。
?
c、設置范圍
設置范圍也是設置多個值,例如指定9點到12點每個小時的每分鐘執行任務。
1 crontab(minute='*', hour='9-12')
這里*號是默認值,可以省略如下:
1 crontab(hour='9-12')
上面提到逗號是or邏輯關系。拓展一下,指定9點到12點和20點中每分鐘執行任務:
1 crontab(hour='9-12,20')
crontab的表達式越來越復雜了。celery還提供了一個類得到表達式解析結果,代碼如下:
1 from celery.task.schedules import crontab_parser
2 r = crontab_parser(23, 0).parse('9-12,20')
3 print(r)
其中,crontab_parse是一個解析類。第1個參數是范圍的最大值;第2個參數是范圍的最小值。通過parse輸入表達式,可得到表達式的解析結果:
1 set([9, 10, 11, 12, 20])
下面很多地方我們都可以通過該方法驗證解析結果。
??
d、設置間隔步長
假如我要設置1、3、5、7、9、11月份每天每分鐘執行任務,按照上面的做法可以設置如下:
1 crontab(day_of_month='1,3,5,7,9,11')
觀察數據可以發現,都是間隔2的步長。需要設置的數字比較少,若數字比較多顯得很麻煩。例如我想每間隔2分鐘就執行一次任務,要寫30個數字想想就覺得很麻煩。crontab表達式還提供了間隔的處理,例如:
1 crontab(minute='*/2')
2 crontab(minute='0-59/2') #效果等同上面
這個/號不是除以的意思。相當與range的第3個參數,例如:
1 range(0, 59+1, 2)
差不多crontab表達式就這些,多舉幾個例子:
?
1 #每2個小時中每分鐘執行1次任務2 crontab(hour='*/2')3 4 #每3個小時的0分時刻執行1次任務5 #即[0,3,6,9,12,15,18,21]點0分6 crontab(minute=0, hour='*/3')7 8 #每3個小時或8點到12點的0分時刻執行1次任務9 #即[0,3,6,9,12,15,18,21]+[8,9,10,11,12]點0分
10 crontab(minute=0, hour='*/3,8-12')
11
12 #每個季度的第1個月中,每天每分鐘執行1次任務
13 #月份范圍是1-12,每3個月為[1,4,7,10]
14 crontab(month_of_year='*/3')
15
16 #每月偶數天數的0點0分時刻執行1次任務
17 crontab(minute=0, hour=0, day_of_month='2-31/2')
18
19 #每年5月11號的0點0分時刻執行1次任務
20 crontab(0, 0, day_of_month='11', month_of_year='5')