前幾天在工作中遇到djcelery定時任務失效的問題,查了好幾天,最終定位解決,整理分享下
首先簡單介紹下djcelery定時任務的框架,估計了解它的人都很熟悉,如下圖

其實簡單的說就是celery的beat定時將任務發給消息中間件(這里用的是rabbitmq隊列),rabbitmq將定時任務發送給celery worker去執行(即django中的task),最終將檢測結果存儲的過程。
關於celery的詳細介紹可見http://python.jobbole.com/87086/
問題現象如下:
發現一直正常運行的定時任務沒有正常執行,而celery的worker和beat進程都在后台運行中,即如下的進程是存在的

故曾經的運維經驗告訴我要重啟,所以,第一招,重啟celery的worker和beat進程,但發現問題依舊,也就是說這個問題沒法通過守護進程的方式避免。
那第二招就是看日志了,查看worker的日志,發現其實worker已經准備好,但一直沒有真正去執行任務

下一步就是查看發生任務的消息中間件了,就是神奇的rabbitmq,rabbitmq的情況可在網頁查看,如下圖


可以明顯的看到queue message是沒有數據的,但message rates卻是有數據的(這個點其實還是沒弄清到底是為什么,歡迎比較懂的同學指點)
故嘗試手動執行celery beat和worker,rabbitmq是否能收到任務,發現在djcelery的任務沒有發送到rabbitmq中,手動編寫測試task程序
celeryconfig.py
import sys import os sys.path.insert(0, os.getcwd()) CELERY_IMPORTS = ("tasks", ) CELERY_RESULT_BACKEND = "amqp" BROKER_HOST = "localhost" BROKER_PORT = 5672 BROKER_USER = "guest" BROKER_PASSWORD = "guest" BROKER_VHOST = "/"
tasks.py
from celery.task import task from celery import Celery, platforms platforms.C_FORCE_ROOT = True @task def add(x, y): return x + y
執行任務,發現rabbitmq可以接受到消息,由於之前對隊列的了解相當少,這里也請教了些對隊列比較了解的同學,都覺得應該是消費者配置的問題,但查了django的setting沒看到異常。
查了下網上關於rabbitmq的資料,大概是生產者通過exchange將消息按照規則發送給對應的隊列,而我們的隊列是沒有收到消息的,修改了exchange規則調試,確實隊列中會收到消息,但這個隊列不是我們系統制定的隊列。
在和rabbitmq糾纏了好久后,各種執行celery的命令,發現手動單獨執行celery beat時會出現如下報錯

懷疑與定時任務有關,查看了報錯的celery的schedules.py文件中報錯的位置,初步判斷是定時任務設定的問題,通過django的admin查看Periodic task的任務設置情況,發現有一個測試任務沒有設置crontab和Interval,之前就遇到過當進入到這個頁面,如果兩個都不設置報錯就會報錯的情況,故將interval改為定時監測時間,crontab不設置后,celery worker日志中有數據了,rabbitmq的隊列也恢復正常了。

其實這個問題在這個文檔中有一些說明
https://my.oschina.net/kinegratii/blog/292395?fromerr=2lvw3H0L

定時任務的重點在於必須有且僅有interval和crontab中的其一設置,即不能同時設置也不能同時不設置

celery很強大,更多的情況可直接參考上述的兩個文檔,基本夠用了
而為什么會出現測試任務的定時設置的crontab和interval都為空的情況呢?由於我們的系統使用的開源項目為頁面式的,在調試新功能時增加的測試任務,而修改沒有涉及到后續任務執行情況的改動,故沒有在admin頁面設定interval和crontab,導致celery beat啟動報錯。
為了避免這類的問題再次出現,進行如下的一些改進
1、在系統頁面中增加添加任務后自動設置默認的crontab和interval(省得人工去django的admin改,當然,django的admin很強大)
2、增加celery beat單獨的日志
最后,無變更不故障,這句話我是真的體會到了
