項目中有使用到Celery框架,主要使用Celery來在使用Django搭建的項目中創建延時任務及周期任務。在使用過程中出現過延時任務及周期任務到預定時間未能執行的情況。Google、百度了壹些網友的分析及解決方案,大多認為是Celery時區設置導致的問題。然而這些解答大多類似,而且並不能解決我心中的疑惑,因此決定研究源碼壹探究竟。
這裏網上大多數解答存在問題的地方,將延時任務及周期任務混為壹談了。周期任務是存在壹個周期,定時執行的任務,類似Linux系統的Crontab定時任務。而延時任務更類似壹個普通的異步任務,不同的是存在壹個ETA延時時間,這種任務只會執行壹次。因此我們會分開討論兩種任務。
celery存在兩個時區的配置 enable_utc 及 timezone ,前者表示是否使用UTC時間,後者表示celery使用的時區。celery默認使用UTC時間,若使用默認配置,則celery設置周期任務時,必須使用UTC時間,比如
當系統時間是北京時間時,這樣的設置會導致這個任務並不會在每天北京時間9:30執行,而是17:30,因為UTC時間和北京時間相差8小時。因此這裏我們將配置修改為
這樣周期任務就能正常執行了
大多數網上的解答止步於上述的結果,認為上述設置之後,延時任務就同樣沒有任何問題了,其實不然,例如
我們準備讓這個任務在 2021-08-19 18:10:30 去執行,結果呢
當req.utc為True時,執行to_system_tz方法轉換eta,否則直接使用Celery設置的時區轉換,這個req.utc又是什麽?
從這裏可以看出,當我們設置了args參數時,由hybrid_to_proto2返回,繼續
omg!!!原來apply_async方法還有壹個參數叫utc嗎,而且默認值是True,看文檔的時候並沒有註意到!到這裏,我們大概能看出問題的所在了,延時任務也可以認為普通的異步任務,存在自己的時區配置參數,之前提到的enable_utc並不能影響到這裏。我們再看看傳入的eta做了哪些處理。
傳入的native類型的時間,居然直接被轉換成UTC時間了!!!
討論了這麽多,我們能得出最終解決Celery時區問題的結論
1、對於周期任務,需要將celery的配置enable_utc設置成False,timezone設置成系統當前的時區
2、對於延時任務,如果需要設置eta,即精確在某壹時間執行,則這個eta必須包含時區信息
最終結論相當簡單,但是花了大量時間去研究了源碼。對於延時任務eta設置曾經也是相當困惑,其實對於官方文檔確實有提到,eta必須要包含時區信息,之前並沒有註意到