Python定時任務-APScheduler

目錄

一,簡介

1,安裝APSchedule 3.6.0

2,APScheduler四個組件

二,選擇調度器、作業存儲、執行器和觸發器

1,schedulers調度器選擇

2,job stores作業存儲選擇

3,executors執行器選擇

4,triggers觸發器選擇

三,調度器scheduler啓動關閉和配置

1,啓動調度器

2,關閉調度器

3,配置調度器

四,job操作

1,job添加

2,job移除

3,job暫停和恢復

4,job列表獲取

5,job修改

五,簡單示例運行

1,APScheduler 啓用3步驟

2,blocking 類型調度器示例

3,background 類型調度器示例

4,觸發器示例

六,其它

七,附錄

1,整體結構

2,支持與以下5種框架集成


彈性伸縮需實現定時伸縮功能,對比了幾個實現方案python-crontab、celery和APScheduler,最終選定APScheduler.

官方文檔:https://apscheduler.readthedocs.io/en/v3.6.0/index.html

Github源碼:https://github.com/agronholm/apscheduler

 

一,簡介

Advanced Python Scheduler (APScheduler) 是一個Python庫,可實現延遲調度要執行Python代碼的功能,可以只執行一次,也可以定期執行。可以隨時添加新任務或刪除舊任務。如果將job任務存儲在數據庫中,這些任務還將在重新啓動調度程序後保持它們的狀態並繼續運行。當重新啓動調度程序時,它將運行離線時應該運行的所有job任務。

 

1,安裝APSchedule 3.6.0

直接pip聯網安裝,或者下載離線包安裝:https://pypi.org/project/APScheduler/3.6.0/ ,手動安裝APScheduler,手動安裝需要安裝依賴包funcsigs、zlocal

$ pip install apscheduler

2,APScheduler四個組件

1,triggers觸發器

包含調度邏輯,每一個job有它自己的觸發器,用於決定job下一次運行時間。除了初始配置外,觸發器完全是無狀態的。

2,job stores作業存儲

存儲被調度的job,默認的job存儲是簡單地把job存儲在內存中,其他的job存儲是保存在數據庫中。Job的數據在保存持久化存儲時被序列化,並在加載時進行反序列化。job存儲(默認存儲除外)不將job數據保存在內存中,而是充當後臺保存、加載、更新和搜索job的中間人。job存儲永遠不能在調度程序之間共享。

3,executors執行器

負責處理job的運行,通過將job指定的可調用對象 提交給一個線程或進程池來運行。當job完成時,執行器將會通知調度器,然後調度程序發出相應event

4,schedulers調度器

一個應用程序中通常只有一個調度器在運行,應用程序開發人員通常不會直接處理job存儲、執行器和觸發器,相反,調度器程序提供了處理這些事件的接口。

配置job存儲和執行器都是在調度器中完成,例如添加、修改和移除job

 

二,選擇調度作業存儲、執行和觸發器

 

1,schedulers調度器選擇

對調度程序的選擇主要取決於當前的編程環境,還有使用APScheduler的目的場景,以下7種調度器可選:

BlockingScheduler : 調度器在當前進程的主線程中運行,也就是會阻塞當前線程。

BackgroundScheduler : 調度器在後臺線程中運行,不會阻塞當前線程。(在沒有使用下面5個框架時使用

AsyncIOScheduler : 結合 asyncio 模塊(一個異步框架)一起使用。

GeventScheduler : 程序中使用 gevent(高性能的Python併發框架)作爲IO模型,和 GeventExecutor 配合使用。

TornadoScheduler : 程序中使用 Tornado(一個web框架)的IO模型。

TwistedScheduler : 配合 TwistedExecutor使用

QtScheduler : 配合 Qt 應用使用

 

2,job stores作業存儲選擇

要選擇適當的作業存儲,首先要確定是否需要作業數據持久化。如果總是在應用程序開始時重新創建作業,那麼作業存儲可以選擇默認方式(MemoryJobStore)。否則,選擇對應的持久化存儲方式。jobstore提供對scheduler中job的增刪改查接口,根據存儲backend的不同,分以下幾種

MemoryJobStore:沒有序列化,jobs就存在內存裏,增刪改查也都是在內存中操作

SQLAlchemyJobStore:所有sqlalchemy支持的數據庫都可以做爲backend,增刪改查操作轉化爲對應backend的sql語句

MongoDBJobStore:用mongodb作backend

RedisJobStore: 用redis作backend

RethinkDBJobStore: 用rethinkdb 作backend

ZooKeeperJobStore:用ZooKeeper做backend

 

3,executors執行器選擇

如果使用上面的5個框架之一,通常選擇對應框架的executor。否則,默認使用的ThreadPoolExecutor就可以滿足大多數場景。如果工作涉及CPU密集型操作,則應該考慮使用ProcessPoolExecutor來充分利用多個CPU內核。可以同時使用這兩種方法,將進程池執行器添加爲輔助執行器。

最常用的兩種executor :ProcessPoolExecutor 和 ThreadPoolExecutor,其它的還有AsyncIOExecutorDebugExecutor一種特殊的執行程序,直接執行可調用的目標,而不是將其延遲給線程或進程)、GeventExecutorTwistedExecutor

 

4,triggers觸發器選擇

調度一個job時,需要爲它選擇一個trigger觸發器。觸發器決定在運行job時計算日期/時間的邏輯。APScheduler有三種內置的觸發器類型:

1)date: 指定某個確定的時間job僅執行一次
2)interval: 指定時間間隔(fixed intervals)週期性執行。
3)cron: 使用cron風格表達式週期性執行,用於(在指定時間內)定期運行job的場景

4)combining還可以將多個觸發器組合成一個觸發器,該觸發器可以按所有參與觸發器約定的時間觸發,也可以在其中任何一個觸發器將觸發的時候觸發。參考combiningAndTriggerOrTrigger實現。

 

三,調度器scheduler啓動關閉和配置

1,啓動調度器

啓動調度器只需調用調度器上的start()。除了BlockingScheduler以外的調度程序,此調用將立即返回,可以繼續應用程序的初始化過程,例如向調度程序添加作業。

對於BlockingScheduler,只需要在完成任何初始化步驟之後調用start()。

注意:啓動調度程序後,不能再更改其設置。

2,關閉調度器

scheduler.shutdown()

默認情況下,調度程序關閉其作業存儲和執行器,並等待所有當前執行的作業完成。如果你不想等,你可以執行:

scheduler.shutdown(wait=False)

這仍然會關閉作業存儲和執行器,但不會等待任何正在運行的任務完成。

3,配置調度器

APScheduler提供了許多不同的方法來配置調度程序。可以使用配置字典,也可以將選項作爲關鍵字參數傳入。還可以先實例化調度器,然後添加作業並配置調度器。通過這種方式,可以爲任何環境獲得最大的靈活性。

1)默認配置:

假設在你的應用程序中運行BackgroundScheduler默認的job存儲和默認的executors執行程序,如下:

from apscheduler.schedulers.background import BackgroundScheduler

scheduler = BackgroundScheduler()

此時將得到一個BackgroundScheduler實例,其中MemoryJobStore爲“default”作業存儲方法,ThreadPoolExecutor爲“default”執行器,默認最大線程數爲10。(參考apscheduler\executors\pool.py的ThreadPoolExecutor

2)高級配置:

希望使用兩個executor擁有兩個作業存儲,還希望爲新作業調整默認值並設置不同的時區。配置可實現如下:

from pytz import utc
from apscheduler.schedulers.background import BackgroundScheduler
from apscheduler.jobstores.mongodb import MongoDBJobStore
from apscheduler.jobstores.sqlalchemy import SQLAlchemyJobStore
from apscheduler.executors.pool import ThreadPoolExecutor, ProcessPoolExecutor

jobstores = {
    'mongo': MongoDBJobStore(),
    'default': SQLAlchemyJobStore(url='sqlite:///jobs.sqlite')
}
executors = {
    'default': ThreadPoolExecutor(20),
    'processpool': ProcessPoolExecutor(5)
}
job_defaults = {
    'coalesce': False,
    'max_instances': 3
}
scheduler = BackgroundScheduler(jobstores=jobstores, executors=executors, job_defaults=job_defaults, timezone=utc)

其中,url指定數據庫的連接,運行後會直接在指定的數據庫中添加apscheduler_jobs表,保存job相關信息。

 

四,job操作

Job是APScheduler的核心,其承接當前需要執行的工作和任務,可以在系統運行過程中動態地進行增加/修改/刪除/查詢等操作。

 

1,job添加

共有兩種方式進行新增job的操作:

1)基於add_job來動態增加

代碼示例:

sched.add_job(job_function, 'cron', day_of_week='mon-fri', hour=5, minute=30, end_date='2014-05-30')

2)基於修飾器scheduled_job來動態裝飾job的實際函數

代碼示例:

@sched.scheduled_job('cron', id='my_job_id', day='last sun')   

def some_decorated_task():

print("I am printed at 00:00:00 on the last Sunday of every month!")

內置的作業存儲中,只有MemoryJobStore不會序列化作業。在內置的執行器中,只有ProcessPoolExecutor會序列化作業。

注意:如果在應用程序初始化期間在持久性作業存儲中調度作業,則**必須**爲作業定義顯式ID,並使用'replace_existing=True'否則每次重新啓動應用程序時,都會得到一份新的作業副本!

2job移除

可以通過job對象調用remove刪除。

也可通過scheduler對象,指定job id調用remove_job刪除;scheduler對象還可調用remove_all_jobs刪除所有job。

示例1:

job = scheduler.add_job(myfunc, 'interval', minutes=2)

job.remove()

示例2:

scheduler.add_job(myfunc, 'interval', minutes=2, id='my_job_id')

scheduler.remove_job('my_job_id') 

3,job暫停和恢復

可以通過作業實例或調度程序本身對作業執行暫停和恢復操作。當作業暫停時,它的下一個運行時被清除,並且在作業恢復之前不會計算它的進一步運行時。

暫停作業:

apscheduler.job.Job.pause()

apscheduler.schedulers.base.BaseScheduler.pause_job()

恢復作業:

apscheduler.job.Job.resume()

apscheduler.schedulers.base.BaseScheduler.resume_job()

4,job列表獲取

要獲得調度作業處理列表,可以使用get_jobs()方法。它將返回所有job實例。如果只需要獲取特定作業存儲庫中包含的作業,將作業存儲別名作爲第二個參數。

爲了方便,可以使用print_jobs()方法,該方法將打印出格式化的作業列表、它們的觸發器和下一次運行時。

apscheduler.get_jobs()

5,job修改

可以通過apscheduler.job.Job.modify() apscheduler.modify_job()修改除了id之外的job屬性。例如:

job.modify(max_instances=6, name='Alternate name')

如果你想修改job的調度器,也就是說,改變它的觸發器你可以使用apscheduler.job.Job.reschedule() reschedule_job()

scheduler.reschedule_job('my_job_id', trigger='cron', minute='*/5')

 

五,簡單示例運行

 

1,APScheduler 3步驟

1)新建scheduler調度器(選擇一種scheduler執行實例化操作)

2)向調度器添加一個job調度任務

3)運行job調度任務

 

2,blocking 類型調度器示例

演示使用blocking阻塞調度程序來調度每隔3秒執行一次的作業。

from datetime import datetime
import os
from apscheduler.schedulers.blocking import BlockingScheduler
def tick():
    print('Tick! The time is: %s' % datetime.now())

if __name__ == '__main__':
    scheduler = BlockingScheduler()
    scheduler.add_job(tick, 'interval', seconds=3)
    print('Press Ctrl+{0} to exit'.format('Break' if os.name == 'nt' else 'C'))
    try:
        scheduler.start()
    except (KeyboardInterrupt, SystemExit):
        pass

3,background 類型調度器示例

演示使用background後臺調度程序來調度每隔3秒執行一次的作業。

from datetime import datetime
import time
import os
from apscheduler.schedulers.background import BackgroundScheduler
def tick():
    print('Tick! The time is: %s' % datetime.now())

if __name__ == '__main__':
    scheduler = BackgroundScheduler()
    scheduler.add_job(tick, 'interval', seconds=3)
    scheduler.start()
    print('Press Ctrl+{0} to exit'.format('Break' if os.name == 'nt' else 'C'))
    try:
        # This is here to simulate application activity (which keeps the main thread alive).
        while True:
            time.sleep(2)
    except (KeyboardInterrupt, SystemExit):
        # Not strictly necessary if daemonic mode is enabled but should be done if possible
        scheduler.shutdown()

4,觸發器示例

1)date觸發器,特定的時間點觸發作業任務只會執行一次。

# 在 2019-03-29 14:00:00 時刻運行一次 job_func 方法
scheduler.add_job(job_func, 'date', run_date=datetime(2019, 3, 29, 14, 0, 0), args=['text'])

其中,run_date賦值類型可以爲date/datetime對象,或符合ISO_8601時間格式的字符串。其它時間格式及場景格式如下:

sched.add_job(my_job, 'date', run_date=date(2019, 3, 29), args=['text'])
sched.add_job(my_job, 'date', run_date='2019-03-29 14:30:05', args=['text'])
sched.add_job(my_job, args=['text']) #立即運行

 2)interval 觸發器固定時間間隔觸發。

# 在 2019-03-29 14:00:01 ~ 2019-03-29 14:00:10 之間, 每隔兩分鐘執行一次job_func方法。
scheduler.add_job(job_func, 'interval', minutes=2, start_date='2019-03-29 14:00:01' , end_date='2019-03-29 14:00:10')

其中,start_dateend_date賦值類型可以爲date/datetime對象,或符合ISO_8601時間格式的字符串。其它時間格式及場景格式如下:

sched.add_job(job_function, 'interval', hours=2) #持續定時觸發
sched.add_job(job_function, 'interval', hours=1, jitter=120) #使用jitter參數,用於在執行時間中添加一個隨機組件,用於多服務器場景中防止同時運行一個job的場景。此時將額外延遲[-120,+120]

3)cron 觸發器在特定時間週期性地觸發,和Linux crontab格式兼容

# 在2019-03-30 00:00:00之前,每週一到週五的5:30(am)觸發
sched.add_job(job_function, 'cron', day_of_week='mon-fri', hour=5, minute=30, end_date='2019-03-30')

其中,start_dateend_date賦值類型可以爲date/datetime對象,或符合ISO_8601時間格式的字符串。其它時間格式及場景格式如下:

sched.add_job(job_function, 'cron', month='6-8,11-12', day='3rd fri', hour='0-3')  #在六月七月八月十一月十二月的第三個週五的0點1點2點3點執行
sched.add_job(job_function, CronTrigger.from_crontab('0 0 1-15 may-aug *'))  #使用標準crontab表達式
sched.add_job(job_function, 'cron', hour='*', jitter=120)  #使用jitter參數,同上

 

六,其它

1,max_instances參數

限制一個作業併發執行實例的數量。(add_job方法)

默認情況下,每個作業只能同時運行一個實例。這意味着,如果有作業即將運行,但前一個運行尚未完成,則最新的運行將無效。通過在添加作業時使用max_instances關鍵字參數,可以爲調度程序設置允許併發運行的特定作業的最大實例數。

2,misfire_grace_time參數

有時,調度程序可能無法在調度時執行計劃的作業。最常見的情況是,在持久化作業存儲中調度作業,並且在作業應該執行之後關閉並重新啓動調度程序。當這種情況發生時,job被認爲是“失敗的”。調度程序將根據作業的misfire_grace_time選項(可以根據每個作業或調度程序中的全局設置該選項)檢查每個錯過的執行時間,以確定是否仍然應該觸發執行。這可能導致作業連續執行幾次。

如果你的特定用例不希望出現這種行爲,那麼可以使用coalescing 合併將所有這些未執行的操作合併到一起。換句話說,如果爲作業啓用了合併,並且調度程序看到作業的一個或多個隊列執行,它將只觸發一次。對於“bypassed”運行,不會發送無效事件。

如果一個作業的執行由於池中沒有線程或進程可用而延遲,那麼執行器可能會因爲它運行得太晚而跳過它(與它最初指定的運行時相比)。如果在您的應用程序中可能發生這種情況,可以增加執行器中的線程/進程數量,或者將misfire_grace_time設置調整爲更高的值。(add_job方法)

3,事件調度器

可以監聽調度、任務執行情況相關的事件。

def my_listener(event):
    if event.exception:
        print('The job crashed :(')
    else:
        print('The job worked :)')
scheduler.add_listener(my_listener, EVENT_JOB_EXECUTED | EVENT_JOB_ERROR)

4,故障排查

如果調度器沒有按照預期工作,可以將apscheduler日誌記錄器的日誌級別提高到調試級別。

如果還沒有啓用日誌功能,可以這樣做:

import logging
logging.basicConfig()
logging.getLogger('apscheduler').setLevel(logging.DEBUG)

 

七,附錄

 

1,整體結構

    apscheduler.events

    apscheduler.executors.asyncio

    apscheduler.executors.base

    apscheduler.executors.debug

    apscheduler.executors.gevent

    apscheduler.executors.pool

    apscheduler.executors.twisted

    apscheduler.job

    apscheduler.jobstores.base

    apscheduler.jobstores.memory

    apscheduler.jobstores.mongodb

    apscheduler.jobstores.redis

    apscheduler.jobstores.rethinkdb

    apscheduler.jobstores.sqlalchemy

    apscheduler.jobstores.zookeeper

    apscheduler.schedulers

    apscheduler.schedulers.asyncio

    apscheduler.schedulers.background

    apscheduler.schedulers.base

    apscheduler.schedulers.blocking

    apscheduler.schedulers.gevent

    apscheduler.schedulers.tornado

    apscheduler.schedulers.twisted

    apscheduler.triggers.base

    apscheduler.triggers.combining

    apscheduler.triggers.cron

    apscheduler.triggers.date

    apscheduler.triggers.interval

2,支持與以下5種框架集成

asyncio
gevent
Tornado
Twisted
Qt
 

參考:

https://apscheduler.readthedocs.io/en/latest/userguide.html

https://github.com/agronholm/apscheduler/tree/master/docs

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章