APScheduler是基於Quartz的一個Python定時任務架構,實現了Quartz的所有功能,使用起來十分方便。提供了基於日期、固定時間間隔以及crontab類型的任務,並且可以持久化任務。基於這些功能,我們可以很方便的實現一個python定時任務系統,寫python還是要比java舒服多了。
1. 定時任務例子
APScheduler是進程內的調度器,可以定時觸發具體的函數,並且可以訪問應用的所有變數和函數。在web應用中通過APScheduler實現定時任務是很方便的。下面看例子:
from apscheduler.scheduler import Scheduler schedudler = Scheduler(daemonic = False) @schedudler.cron_schedule(second='*', day_of_week='0-4', hour='9-12,13-15') def quote_send_sh_job(): print 'a simple cron job start at', datetime.datetime.now() schedudler.start()
上面通過裝飾器定義了cron job,可以通過函數scheduler.add_cron_job添加,用裝飾器更方便。Scheduler建構函式中傳入daemonic參數,表示執行線程是非守護的,在Schduler的文檔中推薦使用非守護線程:(Jobs are always executed in non-daemonic threads. )
定時任務的三種方式:
(1)simple date-based scheduling(定時任務,時間固定,執行一次)
from datetime import datefrom apscheduler.scheduler import Scheduler# Start the schedulersched = Scheduler()sched.start()# example: 需求:在 2013-1-4 13:14:21 列印 i love you</span>def my_job(text): print text# Store the job in a variable in case we want to cancel it# 方法的第一個參數是需要執行的方法名,第二個參數是時間,第三個參數是需要執行的方法的參數列表job = sched.add_date_job(my_job, '2013-01-04 13:14:21', ['i love you'])</span>
(2)Interval-based scheduling(每隔多長時間執行一次)
# example: 需求:每隔一個小時列印一次hello world</span>job = sched.add_interval_job(my_job,hour=1,['hellow world'])
(3)cron-style scheduling(定時迴圈執行,比如每個月的幾號,或者每周幾,或者一年中的第幾周執行)
# 沒有設定時分秒預設為0,example: 需求: 每周一,三,五列印hello world(周日是0,周六是6)job = sched.add_cron_job(my_job,day-of-week='0,2,4',['hellow world'])
更多參數挪步官網:https://apscheduler.readthedocs.org/en/v2.1.0/modules/scheduler.html
在添加job時還有一個比較重要的參數max_instances,指定一個job的並發執行個體數,預設值是1。預設情況下,如果一個job準備執行,但是該job的前一個執行個體尚未執行完,則後一個job會失敗,可以通過這個參數來改變這種情況。
2. 儲存任務執行資訊
APScheduler提供了jobstore用於儲存job的執行資訊,預設使用的是RAMJobStore,還提供了SQLAlchemyJobStore、ShelveJobStore和MongoDBJobStore。APScheduler允許同時使用多個jobstore,通過別名(alias)區分,在添加job時需要指定具體的jobstore的別名,否則使用的是別名是default的jobstore,即RAMJobStore。下面以MongoDBJobStore舉例說明。
import pymongo from apscheduler.scheduler import Scheduler from apscheduler.jobstores.mongodb_store import MongoDBJobStore import time sched = Scheduler(daemonic = False) mongo = pymongo.Connection(host='127.0.0.1', port=27017) store = MongoDBJobStore(connection=mongo) sched.add_jobstore(store, 'mongo') # 別名是mongo @sched.cron_schedule(second='*', day_of_week='0-4', hour='9-12,13-15', jobstore='mongo') # 向別名為mongo的jobstore添加job def job(): print 'a job' time.sleep(1) sched.start()
注意start必須在添加job動作之後調用,否則會拋錯。預設會把job資訊儲存在apscheduler資料庫下的jobs表:
> db.jobs.findOne() { "_id" : ObjectId("502202d1443c1557fa8b8d66"), "runs" : 20, "name" : "job", "misfire_grace_time" : 1, "coalesce" : true, "args" : BinData(0,"gAJdcQEu"), "next_run_time" : ISODate("2012-08-08T14:10:46Z"), "max_instances" : 1, "max_runs" : null, "trigger" : BinData(0,"xxx..."), "func_ref" : "__main__:job", "kwargs" : BinData(0,"gAJ9cQEu") }
上面就是儲存的具體資訊。
3.異常處理
當job拋出異常時,APScheduler會默默的把他吞掉,不提供任何提示,這不是一種好的實踐,我們必須知曉程式的任何差錯。APScheduler提供註冊listener,可以監聽一些事件,包括:job拋出異常、job沒有來得及執行等。
看下面的例子,監聽異常和miss事件,這裡用logging模組列印日誌,logger.exception()可以列印出異常堆棧資訊。
def err_listener(ev): err_logger = logging.getLogger('schedErrJob') if ev.exception: err_logger.exception('%s error.', str(ev.job)) else: err_logger.info('%s miss', str(ev.job)) schedudler.add_listener(err_listener, apscheduler.events.EVENT_JOB_ERROR | apscheduler.events.EVENT_JOB_MISSED)
事件的屬性包括:
job – the job instance in questionscheduled_run_time – the time when the job was scheduled to be runretval – the return value of the successfully executed jobexception – the exception raised by the jobtraceback – the traceback object associated with the exception
最後,需要注意一點當job不以daemon模式運行時,並且APScheduler也不是daemon的,那麼在關閉指令碼時,Ctrl + C是不奏效的,必須kill才可以。可以通過命令實現關閉指令碼:
ps axu | grep {指令碼名} | grep -v grep | awk '{print $2;}' | xargs kill