1. APScheduler簡介
APscheduler全稱Advanced Python Scheduler
作用爲在指定的時間規則執行指定的作業。
-
指定時間規則的方式可以是間隔多久執行,可以是指定日期時間的執行,也可以類似Linux系統中Crontab中的方式執行任務。
-
指定的任務就是一個Python函數。
2. APScheduler組件
APScheduler版本 3.6.3
2.1. APScheduler中幾個重要的概念
2.1.1. Job 作業
作用
Job作爲APScheduler最小執行單位。
創建Job時指定執行的函數,函數中所需參數,Job執行時的一些設置信息。
構建說明
id:指定作業的唯一ID
name:指定作業的名字
trigger:apscheduler定義的觸發器,用於確定Job的執行時間,根據設置的trigger規則,計算得到下次執行此job的
時間, 滿足時將會執行
executor:apscheduler定義的執行器,job創建時設置執行器的名字,根據字符串你名字到scheduler獲取到執行此
job的 執行器,執行job指定的函數
max_instances:執行此job的最大實例數,executor執行job時,根據job的id來計算執行次數,根據設置的最大實例數
來確定是否可執行
next_run_time:Job下次的執行時間,創建Job時可以指定一個時間[datetime],不指定的話則默認根據trigger獲取觸
發時間
misfire_grace_time:Job的延遲執行時間,例如Job的計劃執行時間是21:00:00,但因服務重啓或其他原因導致
21:00:31才執行,如果設置此key爲40,則該job會繼續執行,否則將會丟棄此job
coalesce:Job是否合併執行,是一個bool值。例如scheduler停止20s後重啓啓動,而job的觸發器設置爲5s執行
一次,因此此job錯過了4個執行時間,如果設置爲是,則會合併到一次執行,否則會逐個執行
func:Job執行的函數
args:Job執行函數需要的位置參數
kwargs:Job執行函數需要的關鍵字參數
2.1.2. Trigger 觸發器
Trigger綁定到Job,在scheduler調度篩選Job時,根據觸發器的規則計算出Job的觸發時間,然後與當前時間比較
確定此Job是否會被執行,總之就是根據trigger規則計算出下一個執行時間。
Trigger有多種種類,指定時間的DateTrigger,指定間隔時間的IntervalTrigger,像Linux的crontab
一樣的CronTrigger
目前APScheduler支持觸發器:
DateTrigger
IntervalTrigger
CronTrigger
2.1.3. Executor 執行器
Executor在scheduler中初始化,另外也可通過scheduler的add_executor動態添加Executor。
每個executor都會綁定一個alias,這個作爲唯一標識綁定到Job,在實際執行時會根據Job綁定的executor
找到實際的執行器對象,然後根據執行器對象執行Job
Executor的種類會根據不同的調度來選擇,如果選擇AsyncIO作爲調度的庫,那麼選擇AsyncIOExecutor,如果
選擇tornado作爲調度的庫,選擇TornadoExecutor,如果選擇啓動進程作爲調度,
選擇ThreadPoolExecutor或者ProcessPoolExecutor都可以
Executor的選擇需要根據實際的scheduler來選擇不同的執行器
目前APScheduler支持的Executor:
AsyncIOExecutor
GeventExecutor
ThreadPoolExecutor
ProcessPoolExecutor
TornadoExecutor
TwistedExecutor
2.1.4. Jobstore 作業存儲
Jobstore在scheduler中初始化,另外也可通過scheduler的add_jobstore動態添加Jobstore。每個jobstore都會
綁定一個alias,scheduler在Add Job時,根據指定的jobstore在scheduler中找到相應的jobstore,
並將job添加到jobstore中。
Jobstore主要是通過pickle庫的loads和dumps【實現核心是通過python的__getstate__和__setstate__重寫實現】,
每次變更時將Job動態保存到存儲中,使用時再動態的加載出來,作爲存儲的可以是redis,也可以是數據庫【通過
sqlarchemy這個庫集成多種數據庫】,也可以是mongodb等
目前APScheduler支持的Jobstore:
MemoryJobStore
MongoDBJobStore
RedisJobStore
RethinkDBJobStore
SQLAlchemyJobStore
ZooKeeperJobStore
2.1.5. Event 事件
Event是APScheduler在進行某些操作時觸發相應的事件,用戶可以自定義一些函數來監聽這些事件,
當觸發某些Event時,做一些具體的操作
常見的比如。Job執行異常事件 EVENT_JOB_ERROR。Job執行時間錯過事件 EVENT_JOB_MISSED。
目前APScheduler定義的Event
EVENT_SCHEDULER_STARTED
EVENT_SCHEDULER_START
EVENT_SCHEDULER_SHUTDOWN
EVENT_SCHEDULER_PAUSED
EVENT_SCHEDULER_RESUMED
EVENT_EXECUTOR_ADDED
EVENT_EXECUTOR_REMOVED
EVENT_JOBSTORE_ADDED
EVENT_JOBSTORE_REMOVED
EVENT_ALL_JOBS_REMOVED
EVENT_JOB_ADDED
EVENT_JOB_REMOVED
EVENT_JOB_MODIFIED
EVENT_JOB_EXECUTED
EVENT_JOB_ERROR
EVENT_JOB_MISSED
EVENT_JOB_SUBMITTED
EVENT_JOB_MAX_INSTANCES
2.1.6. Listener 監聽事件
Listener表示用戶自定義監聽的一些Event,當Job觸發了EVENT_JOB_MISSED事件時可以根據需求做一些其他處理。
2.1.7. Scheduler 調度器
Scheduler是APScheduler的核心,所有相關組件通過其定義。scheduler啓動之後,將開始按照配置的任務進行調度。
除了依據所有定義Job的trigger生成的將要調度時間喚醒調度之外。當發生Job信息變更時也會觸發調度。
scheduler可根據自身的需求選擇不同的組件,如果是使用AsyncIO則選擇AsyncIOScheduler,使用tornado則選擇
TornadoScheduler。
目前APScheduler支持的Scheduler:
AsyncIOScheduler
BackgroundScheduler
BlockingScheduler
GeventScheduler
QtScheduler
TornadoScheduler
TwistedScheduler
2.2. Scheduler工作流程圖
這裏重點挑選兩個重要的流程畫一個簡陋的流程圖,來看一下scheduler的工作原理。其一個是添加add job,另一是scheduler每次喚醒調度時的執行過程
2.2.1. Scheduler添加job流程
2.2.2 Scheduler調度流程
3. APScheduler使用示例
AsyncIO調度示例
import asyncio
import datetime
from apscheduler.events import EVENT_JOB_EXECUTED
from apscheduler.executors.asyncio import AsyncIOExecutor
from apscheduler.jobstores.redis import RedisJobStore # 需要安裝redis
from apscheduler.schedulers.asyncio import AsyncIOScheduler
from apscheduler.triggers.interval import IntervalTrigger
from apscheduler.triggers.cron import CronTrigger
# 定義jobstore 使用redis 存儲job信息
default_redis_jobstore = RedisJobStore(
db=2,
jobs_key="apschedulers.default_jobs",
run_times_key="apschedulers.default_run_times",
host="127.0.0.1",
port=6379,
password="test"
)
# 定義executor 使用asyncio是的調度執行規則
first_executor = AsyncIOExecutor()
# 初始化scheduler時,可以直接指定jobstore和executor
init_scheduler_options = {
"jobstores": {
# first 爲 jobstore的名字,在創建Job時直接直接此名字即可
"default": default_redis_jobstore
},
"executors": {
# first 爲 executor 的名字,在創建Job時直接直接此名字,執行時則會使用此executor執行
"first": first_executor
},
# 創建job時的默認參數
"job_defaults": {
'coalesce': False, # 是否合併執行
'max_instances': 1 # 最大實例數
}
}
# 創建scheduler
scheduler = AsyncIOScheduler(**init_scheduler_options)
# 啓動調度
scheduler.start()
second_redis_jobstore = RedisJobStore(
db=2,
jobs_key="apschedulers.second_jobs",
run_times_key="apschedulers.second_run_times",
host="127.0.0.1",
port=6379,
password="test"
)
scheduler.add_jobstore(second_redis_jobstore, 'second')
# 定義executor 使用asyncio是的調度執行規則
second_executor = AsyncIOExecutor()
scheduler.add_executor(second_executor, "second")
# *********** 關於 APScheduler中有關Event相關使用示例 *************
# 定義函數監聽事件
def job_execute(event):
"""
監聽事件處理
:param event:
:return:
"""
print(
"job執行job:\ncode => {}\njob.id => {}\njobstore=>{}".format(
event.code,
event.job_id,
event.jobstore
))
# 給EVENT_JOB_EXECUTED[執行完成job事件]添加回調,這裏就是每次Job執行完成了我們就輸出一些信息
scheduler.add_listener(job_execute, EVENT_JOB_EXECUTED)
# *********** 關於 APScheduler中有關Job使用示例 *************
# 使用的是asyncio,所以job執行的函數可以是一個協程,也可以是一個普通函數,AsyncIOExecutor會根據配置的函數來進行調度,
# 如果是協程則會直接丟入到loop中,如果是普通函數則會啓用線程處理
# 我們定義兩個函數來看看執行的結果
def interval_func(message):
print("現在時間: {}".format(datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")))
print("我是普通函數")
print(message)
async def async_func(message):
print("現在時間: {}".format(datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")))
print("我是協程")
print(message)
# 將上述的兩個函數按照不同的方式創造觸發器來執行
# *********** 關於 APScheduler中有關Trigger使用示例 *************
# 使用Trigger有兩種方式,一種是用類創建使用,另一個是使用字符串的方式
# 使用字符串指定別名, scheduler初始化時已將其定義的trigger加載,所以指定字符串可以直接使用
if scheduler.get_job("interval_func_test", "default"):
# 存在的話,先刪除
scheduler.remove_job("interval_func_test", "default")
# 立馬開始 2分鐘後結束, 每10s執行一次 存儲到first jobstore second執行
scheduler.add_job(interval_func, "interval",
args=["我是10s執行一次,存放在jobstore default, executor default"],
seconds=10,
id="interval_func_test",
jobstore="default",
executor="default",
start_date=datetime.datetime.now(),
end_date=datetime.datetime.now() + datetime.timedelta(seconds=240))
# 先創建tigger
trigger = IntervalTrigger(seconds=5)
if scheduler.get_job("interval_func_test_2", "second"):
# 存在的話,先刪除
scheduler.remove_job("interval_func_test_2", "second")
# 每隔5s執行一次
scheduler.add_job(async_func, trigger, args=["我是每隔5s執行一次,存放在jobstore second, executor = second"],
id="interval_func_test_2",
jobstore="second",
executor="second")
# 使用協程的函數執行,且使用cron的方式配置觸發器
if scheduler.get_job("cron_func_test", "default"):
# 存在的話,先刪除
scheduler.remove_job("cron_func_test", "default")
# 立馬開始 每10s執行一次
scheduler.add_job(async_func, "cron",
args=["我是 每分鐘 30s 時執行一次,存放在jobstore default, executor default"],
second='30',
id="cron_func_test",
jobstore="default",
executor="default")
# 先創建tigger
trigger = CronTrigger(second='20,40')
if scheduler.get_job("cron_func_test_2", "second"):
# 存在的話,先刪除
scheduler.remove_job("cron_func_test_2", "second")
# 每隔5s執行一次
scheduler.add_job(async_func, trigger, args=["我是每分鐘 20s 40s時各執行一次,存放在jobstore second, executor = second"],
id="cron_func_test_2",
jobstore="second",
executor="second")
# 使用創建trigger對象直接創建
print("啓動: {}".format(datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")))
asyncio.get_event_loop().run_forever()
輸出結果部分截取
啓動之後,每隔5s運行一次的JOB
啓動: 2019-12-05 14:13:11
【這部分是定義的協程函數輸出的內容】
現在時間: 2019-12-05 14:13:16
我是協程
我是每隔5s執行一次,存放在jobstore second, executor = second
【這部分是監聽job執行完成之後的回調輸出】
job執行job:
code => 4096
job.id => interval_func_test_2
jobstore=>second
在20s和40s時各執行一次的Job
現在時間: 2019-12-05 14:13:20
我是協程
我是每分鐘 20s 40s時各執行一次,存放在jobstore second, executor = second
job執行job:
code => 4096
job.id => cron_func_test_2
jobstore=>second
每隔10s執行一次的job
現在時間: 2019-12-05 14:13:21
我是普通函數
我是10s執行一次,存放在jobstore default, executor default
現在時間: 2019-12-05 14:13:21
我是協程
我是每隔5s執行一次,存放在jobstore second, executor = second
job執行job:
code => 4096
job.id => interval_func_test
jobstore=>default
job執行job:
code => 4096
job.id => interval_func_test_2
jobstore=>second
每隔5s執行一次的Job
現在時間: 2019-12-05 14:13:26
我是協程
我是每隔5s執行一次,存放在jobstore second, executor = second
job執行job:
code => 4096
job.id => interval_func_test_2
jobstore=>second
每分鐘30s時執行一次
現在時間: 2019-12-05 14:13:30
我是協程
我是 每分鐘 30s 時執行一次,存放在jobstore default, executor default
job執行job:
code => 4096
job.id => cron_func_test
jobstore=>default
總結
apscheduler的工作原理及用法基本這樣。
apscheduler強大的地方是可以集成到tornado,django,flask等框架,也可以單獨運行。比如CronTrigger還有更強大的用法,可以參照官網的cron用法
上面例子只列舉了一些常規用法,其實還有一些更切合實際的用法,利用APSchedulder的特性,動態的添加Job,暫停Job,刪除Job,重啓Job等。先按照功能性質定義好不同的函數,然後開發一個web服務。在web服務中動態操作各種Job,可以想象在監控系統中根據需求添加一些任務,豈不美哉。
有時間將這部分做一個例子再來分享。