轉自:http://blog.csdn.net/raptor/article/details/69218271python
在一個Python web應用中須要定時執行一些任務,因此用了APScheduler這個庫。又由於是用flask這個web框架,因此用了flask-apscheduler這個插件(本質上與直接用APScheduler同樣,這裏不做區分)。web
在開發中直接測試運行是沒有問題的,可是用gunicorn部署之後發生了重複運行的問題:docker
每一個任務在時間到的時刻會同時執行好幾遍。flask
注意了一下重複的數量,偏偏是gunicorn裏配置的worker進程數量,顯然是每一個worker進程都啓動了一份scheduler形成。多線程
能夠想到的方案有幾個:app
--preload
啓動gunicorn,確保scheduler只在loader的時候建立一次通過實踐,只有第三個方案比較好。框架
preload的問題:socket
雖然這樣可使用scheduler建立代碼只執行一次,可是問題也在於它只執行一次,從新部署之後若是用kill -HUP重啓gunicorn,它並不會重啓,甚至整個項目都不會更新。這是preload的反作用,除非重寫部署腳本,徹底重啓應用。函數
單獨進程的問題:測試
也是由於部署麻煩,須要多一套部署方案,雖然用Docker會比較方便,但仍然不喜歡,並且同時維護兩個項目也多出不少沒必要要的事情。
全局鎖是一個較好的方案,但問題在於找一個合適的鎖。
python自帶的多進程多線程鎖方案都須要一個共享變量來維護,可是由於worker進程是被gunicorn的主進程啓動的,並不方便本身維護,因此須要一個系統級的鎖。
在Stackoverflow上看到有人是用了一個socket端口來作鎖實現這個方案,可是我也不喜歡這樣浪費一個寶貴的端口資源。不過這倒給了我一個啓發:
能夠用文件鎖!
因而有了這個解決方案:
import atexit import fcntl from flask_apscheduler import APScheduler def init(app): f = open("scheduler.lock", "wb") try: fcntl.flock(f, fcntl.LOCK_EX | fcntl.LOCK_NB) scheduler = APScheduler() scheduler.init_app(app) scheduler.start() except: pass def unlock(): fcntl.flock(f, fcntl.LOCK_UN) f.close() atexit.register(unlock)
init函數爲flask項目初始化所調用,這裏爲scheduler模塊的初始化部分。
首先打開(或建立)一個scheduler.lock文件,並加上非阻塞互斥鎖。成功後建立scheduler並啓動。
若是加文件鎖失敗,說明scheduler已經建立,就略過建立scheduler的部分。
最後註冊一個退出事件,若是這個flask項目退出,則解鎖並關閉scheduler.lock文件的鎖。