對不起,很久以前寫的,它很可能已經過時了,由於本人又懶得維護,請不要再參考這篇可能過時的文檔了
一.引言
Django是python語言下的一個比較熱門的Web框架,越來越多的企業和開發者使用Django實現自己的Web服務器。在Web服務器開發過程中,有時候我們不僅僅是要實現Web服務器端和用戶端的簡單邏輯交互,還要實現一些定時任務。舉出以下的例子:
-
定期刪除或緩存Redis數據庫的記錄
為了追求更高的數據庫訪問性能,我把Redis作為MySql數據庫的緩存。把常訪問的數據放在Redis中,然后定時存儲到Mysql中。並且把過期的Redis數據刪掉.那么這個時候,就需要定時去完成這個任務。 -
生成報表
打個比方,你有一個Web電商服務器,每天用戶都在會在上面購物。為了很方便的統計出每個用戶每個月的消費金額,你在數據庫中設計了一張月統計報表。然后使用定時任務,在每個月的1號進行統計,檢索數據庫,計算出每個用戶上個月的的消費金額,逐個存儲到月統計報表中。那么這個生成報表的任務就是定時完成的,也就是前面提到的每個月的1號。 -
定時發送消息
再如:當你的網站上用戶生日來臨,你希望在他生日那天,給用戶的郵箱發送生日快樂的祝福。那么這也是定時任務實現的。
上面這些的例子,都是需要定時任務。在Python中,我們使用的Celery模塊完成這項任務。網絡上關於Celery的博文很多,大多博文的邏輯比較混亂,因此就有了這篇博文。希望讀者讀完有個清晰的認識,並且很好的實戰出來,不再對celery定時任務有任何的困惑,這是我這篇博文的初衷。
此篇博文沒有介紹Celery的工作原理,諸如Broker,Worker等等。在實戰之前,這些概念必須要理解清楚。由於網上已經有很多這樣的內容,我在文章結尾處貼出了一些參考文檔,方便讀者學習。
二.Celery,Django和Djcelery
始終明確的是:
Celery是Python的第三方庫,它可以用於是任何的Python的項目中,因為我們始終可以把Celery看成一個獨立的模塊去操縱其它的模塊。因此,我們可以在Django項目中使用的Celery,但值得注意的是,在Django中使用Celery的方式有兩種:
- 僅使用Celery。
- 同時使用Celery + djcelery .
方法1: 相當於中Django中加入了一個Celery的任務腳本,為了操縱Django,因此需要額外在Celery中配置Django環境,才能操作Django的數據庫。
方法2: 由於使用了djcelery,可以在任務中方便的直接操作Django數據庫,而且最終的任務可以在Django的后台數據庫中查看和修改相關的任務。
兩種方法的選擇:
從上面的描述看,方法1比方法2少引入一個djcelery模塊,缺點是需要自己配置與Django結合的環境。而方法2,比較方便省心,還能在Django后台管理自己的任務。因此如果你在Django中使用Celery,我強烈推薦方法2。
此篇博文講述了使用方法2, 但它們兩者本質上是一樣的。兩種方式我都有實踐過, 原先我有打算寫關於姊妹篇(關於方法1)的博文,名字都想好了,叫"Django中使用Celery實現定時任務(不用djcelery)"。但是由於后來以為寫了也沒人看,就懶得更新了,再加上后來離職等因素又疏遠了Django后端,所以姊妹篇的計划就一直擱置了。最初這篇文章發表在CSDN上面,后續收到了一些不錯的反饋,這篇博文就再更新了一次。(但是姊妹篇的計划卻還是被我放棄了)。
閑聊時間已結束,下面是正文。
三. Django目錄結構
首先記得: pip install django-celery
下面展示了一個Django項目的目錄結構示例:
- app
- admin.py
- views.py
- urls.py
- models.py
- **tasks.py ** - pro
- settings.py
- urls.py
- urls.py
- models.py - manage.py
注意,上述目錄中的tasks.py
文件是我新建的,放在app的目錄下,整個Celery任務,我只新建了這一個文件。
四. 配置setting.py
為了設置Celery,我們需要對setting.py文件進行配置,過程如下:
1.加入djcelery
INSTALLED_APPS = (
'django.contrib.admin',
'django.contrib.auth',
'djcelery', #此處是新加入的djcelery
'app',
)
上述 INSTALLED_APPS
中我省略了無關的模塊,注意加入djcelery
即可。
2. 設置celery參數
我在setting.py的文件結尾處,加入了如下的celery參數配置,先貼代碼,再解釋。
import djcelery
djcelery.setup_loader()
BROKER_URL = 'redis://127.0.0.1:6379/6'
CELERY_IMPORTS = ('app.tasks', )
CELERY_TIMEZONE = TIME_ZONE
CELERYBEAT_SCHEDULER = 'djcelery.schedulers.DatabaseScheduler'
# 下面是定時任務的設置,我一共配置了三個定時任務.
from celery.schedules import crontab
CELERYBEAT_SCHEDULE = {
#定時任務一: 每24小時周期執行任務(del_redis_data)
u'刪除過期的redis數據': {
"task": "app.tasks.del_redis_data",
"schedule": crontab(hour='*/24'),
"args": (),
},
#定時任務二: 每天的凌晨12:30分,執行任務(back_up1)
u'生成日報表': {
'task': 'app.tasks.back_up1',
'schedule': crontab(minute=30, hour=0),
"args": ()
},
#定時任務三:每個月的1號的6:00啟動,執行任務(back_up2)
u'生成統計報表': {
'task': 'app.tasks.back_up2',
'schedule': crontab(hour=6, minute=0, day_of_month='1'),
"args": ()
},
}
上述代碼釋義:
當djcelery.setup_loader()運行時,Celery便會去查看INSTALLD_APPS下包含的所有app目錄中的tasks.py文件,找到標記為task的方法,將它們注冊為celery task。
BROKER_URL = 'redis://127.0.0.1:6379/6'
broker是代理人,它負責分發任務給worker去執行。我使用的是Redis作為broker,當然你也可以用其它的broker,比如官方就比較推薦使用RabbitMQ.
有的博客中提到要配置關鍵字:CELERY_RESULT_BACKEND
,例如:
CELERY_RESULT_BACKEND = 'amqp://guest@localhost//' #可以不用寫
我沒有配置這個關鍵字。因為如果沒有配置,此時Django會使用默認的數據庫(也是你指定的orm數據庫),作為它的結果作為它的backend。因此你也可以不用寫,使用Django默認設置的數據庫就很好。
2.
CELERY_IMPORTS = ('app.tasks', )
CELERY_TIMEZONE = TIME_ZONE
CELERYBEAT_SCHEDULER = 'djcelery.schedulers.DatabaseScheduler'
上面第一句是導入目標任務文件,第二句是設置時區,第三句表示使用了django-celery默認的數據庫調度模型,任務執行周期都被存在默認指定的orm數據庫中.
更深入的Celery配置:(http://www.cnblogs.com/ajianbeyourself/p/4950758.html)
3.
from celery.schedules import crontab
CELERYBEAT_SCHEDULE = {
#定時任務一: 每24小時周期執行任務(del_redis_data)
u'刪除過期的redis數據': {
"task": "app.tasks.del_redis_data",
"schedule": crontab(hour='*/24'),
"args": (),
},
上面是設置定時的時間配置,關於crontab
的具體用法,celery的官方文檔講解的十分詳盡(表格):
http://docs.celeryproject.org/en/latest/userguide/periodic-tasks.html
我選的三個任務,是我特意挑選的,非常有代表性。第一個是周期任務,它會每隔一個固定時間周期去執行一次相應的task,比如隔1分鍾,隔1小時等; 第二個和第三個都是定時任務,定時在每個時間點,比如每天的6點,或者定時在每個月的1號。
周期任務和定時任務有小小的差別,這也是crontab的強大之處,它同時支持這兩種。
5.Tasks任務
每個任務本質上就是一個函數,在tasks.py中,寫入你想要執行的函數即可。我的tasks.py如下:我寫的每個任務又臭又長,因此具體的細節就省略了。
# coding=utf-8
from celery import task
@task()
def del_redis_data():
# 此處是一個刪除redis數據的操作。具體略過
@task()
def back_up1():
# 此處是一個備份到日報表的操作。具體略過
@task()
def back_up2():
# 此處是一個生成統計報表的操作。具體略過
如果讀者需要自己實現一個定時任務,那么上述的task函數必然要自己去定義,我只提供了參考。我上面的三個任務,分別對應了setting.py
文件的CELERYBEAT_SCHEDULE
的三個定時配置。
要記住的是,任務只是一個函數,這個函數什么時候調用,取決你在setting.py
中的配置。
6.啟動定時任務
登錄到Django后台,可以看到后台數據庫中看到有任務的參數,效果圖暫略。
然后啟動終端,切換到Django項目的根目錄下,運行:
python manage.py runserver # 啟動web服務
python manage.py celery worker -l info # 啟動celery woker
這條命令用於啟動worker, worker本質上執行任務的線程,就是一個干苦力的工人。
celery beat -A 項目名 -l info # 啟動beat, 執行定時任務.
上面這條任務用於啟動beater,它就像一個領導,負責把任務分發給工人。
到直接,這篇博文基本就結束了,由於兩次更新的時間間隔比較長,最初的思路都記不清了。當然,你肯定有一個重要的疑問,那就是如果任務因為系統重啟或者其它原因崩潰了怎么重啟呢?對於linux 系統,supervisor可以托管這兩條命令,如果任務沒有跑起來,它可以自己重啟啟動任務,這樣,就能保證服務器端的定時任務不會因為一些原因崩潰掉。
7.推薦文章
1.更深入的Celery配置:(http://www.cnblogs.com/ajianbeyourself/p/4950758.html)