工作中遇到一個需求,要在現有系統(airbnb家的開源平台superset)上添加一個定時郵件的功能。
定時郵件功能使用的是apscheduler這個庫,關於怎么用這里就不多贅述了反正網上都有。
主要記錄一個問題,使用過程中發現郵件有的時候會重發兩次,經過研究之后發現是runserver的時候,調度器實例被創建了兩次。
這個主要和系統使用的flask的一個reload機制有關(FLASK_USE_RELOAD = True),reload主要用於代碼的熱更新(簡單解釋就是,當你runserver啟動服務的時候,會先創建一個主進程,主進程再創建一個子進程。子進程是實際運作的系統,而主進程的主要作用在於監聽代碼的改變,當你python install重裝代碼時,主進程探測到代碼的改變,就會自動重啟子進程,達到代碼熱更新的效果)。
因此當你runserver時,會發現代碼被執行了兩遍。但在系統中調度器實例只應被執行一次,這時可以添加判斷條件:if os.environ.get('WERKZEUG_RUN_MAIN') == 'true' ,創建主進程時會發現此變量值為None,而創建子進程時此變量為true,僅當此變量為true時創建調度器實例,即可避免上述問題。
-------------------------------------
2018-01-17 更新:
后來系統換用gunicorn部署,發現這個問題又回來了,而且gunicorn設置多少個worker,啟動apscheduler的代碼就被執行了多少次。
解決方法:
(1)使用--preload啟動gunicorn(這樣會發現代碼在master啟動時執行了一次,而在所有worker啟動前總共也只執行了一次,這樣問題就和之前flask自帶的測試服務器類似了)
(2)gunicorn下會發現os.environ里找不到'WERKZEUG_RUN_MAIN'這個變量了,一個變通的想法是找到master啟動時不會觸發,而worker啟動時才會觸發的代碼段,手動用os.environ.setdefault('RUN_WORKER', 'true')來達到目的
參考文章:
http://simple-is-better.com/news/1039
https://stackoverflow.com/questions/25504149/why-does-running-the-flask-dev-server-run-itself-twice