Python定時任務(下)

圖片來自 unsplash

上篇文章,我們了解到有三種辦法能實現定時任務,但是都無法做到循環執行定時任務。因此,需要一個能夠擔當此重任的庫。它就是APScheduler。

1 簡介

APScheduler的全稱是Advanced Python Scheduler。它是一個輕量級的 Python 定時任務調度框架。APScheduler 支持三種調度任務:固定時間間隔,固定時間點(日期),Linux 下的 Crontab 命令。同時,它還支持異步執行、后臺執行調度任務。

2 安裝

使用 pip 包管理工具安裝 APScheduler 是最方便快捷的。

pip install APScheduler
# 如果出現因下載失敗導致安裝不上的情況,建議使用代理
pip --proxy http://代理ip:端口 install APScheduler

3 使用步驟

APScheduler 使用起來還算是比較簡單。運行一個調度任務只需要以下三部曲。

  1. 新建一個 schedulers (調度器) 。
  2. 添加一個調度任務(job stores)。
  3. 運行調度任務。

下面是執行每 2 秒報時的簡單示例代碼:

import datetime
import time
from apscheduler.schedulers.background import BackgroundScheduler

def timedTask():
    print(datetime.datetime.utcnow().strftime("%Y-%m-%d %H:%M:%S.%f")[:-3])

if __name__ == '__main__':
    # 創建后臺執行的 schedulers
    scheduler = BackgroundScheduler()  
    # 添加調度任務
    # 調度方法為 timedTask,觸發器選擇 interval(間隔性),間隔時長為 2 秒
    scheduler.add_job(timedTask, 'interval', seconds=2)
    # 啟動調度任務
    scheduler.start()
    
    while True:
        print(time.time())
        time.sleep(5)

4 基礎組件

APScheduler 有四種組件,分別是:調度器(scheduler),作業存儲(job store)觸發器(trigger),執行器(executor)。

  • schedulers(調度器)
    它是任務調度器,屬于控制器角色。它配置作業存儲器和執行器可以在調度器中完成,例如添加、修改和移除作業。

  • triggers(觸發器)
    描述調度任務被觸發的條件。不過觸發器完全是無狀態的。

  • job stores(作業存儲器)
    任務持久化倉庫,默認保存任務在內存中,也可將任務保存都各種數據庫中,任務中的數據序列化后保存到持久化數據庫,從數據庫加載后又反序列化。

  • executors(執行器)
    負責處理作業的運行,它們通常通過在作業中提交指定的可調用對象到一個線程或者進城池來進行。當作業完成時,執行器將會通知調度器。

4.1 schedulers(調度器)

我個人覺得 APScheduler 非常好用的原因。它提供 7 種調度器,能夠滿足我們各種場景的需要。例如:后臺執行某個操作,異步執行操作等。調度器分別是:

  • BlockingScheduler : 調度器在當前進程的主線程中運行,也就是會阻塞當前線程。
  • BackgroundScheduler : 調度器在后臺線程中運行,不會阻塞當前線程。
  • AsyncIOScheduler : 結合 asyncio 模塊(一個異步框架)一起使用。
  • GeventScheduler : 程序中使用 gevent(高性能的Python并發框架)作為IO模型,和 GeventExecutor 配合使用。
  • TornadoScheduler : 程序中使用 Tornado(一個web框架)的IO模型,用 ioloop.add_timeout 完成定時喚醒。
  • TwistedScheduler : 配合 TwistedExecutor,用 reactor.callLater 完成定時喚醒。
  • QtScheduler : 你的應用是一個 Qt 應用,需使用QTimer完成定時喚醒。

4.2 triggers(觸發器)

APScheduler 有三種內建的 trigger:
1)date 觸發器
date 是最基本的一種調度,作業任務只會執行一次。它表示特定的時間點觸發。它的參數如下:

參數 說明
run_date (datetime 或 str) 作業的運行日期或時間
timezone (datetime.tzinfo 或 str) 指定時區

date 觸發器使用示例如下:

from datetime import datetime
from datetime import date
from apscheduler.schedulers.background import BackgroundScheduler

def job_func(text):
    print(text)

scheduler = BackgroundScheduler()
# 在 2017-12-13 時刻運行一次 job_func 方法
scheduler .add_job(job_func, 'date', run_date=date(2017, 12, 13), args=['text'])
# 在 2017-12-13 14:00:00 時刻運行一次 job_func 方法
scheduler .add_job(job_func, 'date', run_date=datetime(2017, 12, 13, 14, 0, 0), args=['text'])
# 在 2017-12-13 14:00:01 時刻運行一次 job_func 方法
scheduler .add_job(job_func, 'date', run_date='2017-12-13 14:00:01', args=['text'])

scheduler.start()

2)interval 觸發器
固定時間間隔觸發。interval 間隔調度,參數如下:

參數 說明
weeks (int) 間隔幾周
days (int) 間隔幾天
hours (int) 間隔幾小時
minutes (int) 間隔幾分鐘
seconds (int) 間隔多少秒
start_date (datetime 或 str) 開始日期
end_date (datetime 或 str) 結束日期
timezone (datetime.tzinfo 或str) 時區

interval 觸發器使用示例如下:

import datetime
from apscheduler.schedulers.background import BackgroundScheduler

def job_func(text):
    print(datetime.datetime.utcnow().strftime("%Y-%m-%d %H:%M:%S.%f")[:-3])

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

scheduler.start()

3)cron 觸發器
在特定時間周期性地觸發,和Linux crontab格式兼容。它是功能最強大的觸發器。
我們先了解 cron 參數:

參數 說明
year (int 或 str) 年,4位數字
month (int 或 str) 月 (范圍1-12)
day (int 或 str) 日 (范圍1-31
week (int 或 str) 周 (范圍1-53)
day_of_week (int 或 str) 周內第幾天或者星期幾 (范圍0-6 或者 mon,tue,wed,thu,fri,sat,sun)
hour (int 或 str) 時 (范圍0-23)
minute (int 或 str) 分 (范圍0-59)
second (int 或 str) 秒 (范圍0-59)
start_date (datetime 或 str) 最早開始日期(包含)
end_date (datetime 或 str) 最晚結束時間(包含)
timezone (datetime.tzinfo 或str) 指定時區

這些參數是支持算數表達式,取值格式有如下:


點擊查看大圖

cron 觸發器使用示例如下:

import datetime
from apscheduler.schedulers.background import BackgroundScheduler

def job_func(text):
    print("當前時間:", datetime.datetime.utcnow().strftime("%Y-%m-%d %H:%M:%S.%f")[:-3])

scheduler = BackgroundScheduler()
# 在每年 1-3、7-9 月份中的每個星期一、二中的 00:00, 01:00, 02:00 和 03:00 執行 job_func 任務
scheduler .add_job(job_func, 'cron', month='1-3,7-9',day='0, tue', hour='0-3')

scheduler.start()

4.3 作業存儲(job store)

該組件是對調度任務的管理。
1)添加 job
有兩種添加方法,其中一種上述代碼用到的 add_job(), 另一種則是scheduled_job()修飾器來修飾函數。

這個兩種辦法的區別是:第一種方法返回一個 apscheduler.job.Job 的實例,可以用來改變或者移除 job。第二種方法只適用于應用運行期間不會改變的 job。

第二種添加任務方式的例子:

import datetime
from apscheduler.schedulers.background import BackgroundScheduler

@scheduler.scheduled_job(job_func, 'interval', minutes=2)
def job_func(text):
    print(datetime.datetime.utcnow().strftime("%Y-%m-%d %H:%M:%S.%f")[:-3])

scheduler = BackgroundScheduler()
scheduler.start()

2)移除 job
移除 job 也有兩種方法:remove_job()job.remove()。
remove_job() 是根據 job 的 id 來移除,所以要在 job 創建的時候指定一個 id。
job.remove() 則是對 job 執行 remove 方法即可

scheduler.add_job(job_func, 'interval', minutes=2, id='job_one')
scheduler.remove_job(job_one)

job = add_job(job_func, 'interval', minutes=2, id='job_one')
job.remvoe()

3)獲取 job 列表
通過 scheduler.get_jobs() 方法能夠獲取當前調度器中的所有 job 的列表

  1. 修改 job
    如果你因計劃改變要對 job 進行修改,可以使用Job.modify() 或者 modify_job()方法來修改 job 的屬性。但是值得注意的是,job 的 id 是無法被修改的。
scheduler.add_job(job_func, 'interval', minutes=2, id='job_one')
scheduler.start()
# 將觸發時間間隔修改成 5分鐘
scheduler.modify_job('job_one', minutes=5)

job = scheduler.add_job(job_func, 'interval', minutes=2)
# 將觸發時間間隔修改成 5分鐘
job.modify(minutes=5)

5)關閉 job
默認情況下調度器會等待所有正在運行的作業完成后,關閉所有的調度器和作業存儲。如果你不想等待,可以將 wait 選項設置為 False。

scheduler.shutdown()
scheduler.shutdown(wait=false)

4.4 執行器(executor)

執行器顧名思義是執行調度任務的模塊。最常用的 executor 有兩種:ProcessPoolExecutorThreadPoolExecutor

下面是顯式設置 job store(使用mongo存儲)和 executor 的代碼的示例。
注:本代碼來源于網絡

from pymongo import MongoClient
from apscheduler.schedulers.blocking import BlockingScheduler
from apscheduler.jobstores.mongodb import MongoDBJobStore
from apscheduler.jobstores.memory import MemoryJobStore
from apscheduler.executors.pool import ThreadPoolExecutor, ProcessPoolExecutor
 
 
def my_job():
    print 'hello world'
host = '127.0.0.1'
port = 27017
client = MongoClient(host, port)
 
jobstores = {
    'mongo': MongoDBJobStore(collection='job', database='test', client=client),
    'default': MemoryJobStore()
}
executors = {
    'default': ThreadPoolExecutor(10),
    'processpool': ProcessPoolExecutor(3)
}
job_defaults = {
    'coalesce': False,
    'max_instances': 3
}
scheduler = BlockingScheduler(jobstores=jobstores, executors=executors, job_defaults=job_defaults)
scheduler.add_job(my_job, 'interval', seconds=5)
 
try:
    scheduler.start()
except SystemExit:
    client.close() 

上篇閱讀:Python定時任務(上)
推薦閱讀:徹底理解Iterable、Iterator、generator


最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,345評論 6 531
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,494評論 3 416
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事?!?“怎么了?”我有些...
    開封第一講書人閱讀 176,283評論 0 374
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,953評論 1 309
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,714評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,186評論 1 324
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,255評論 3 441
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,410評論 0 288
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,940評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,776評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,976評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,518評論 5 359
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,210評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,642評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,878評論 1 286
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,654評論 3 391
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,958評論 2 373

推薦閱讀更多精彩內容

  • 《分布式任務調度平臺XXL-JOB》 一、簡介 1.1 概述 XXL-JOB是一個輕量級分布式任務調度框架,其核心...
    許雪里閱讀 16,806評論 3 29
  • Spring Cloud為開發人員提供了快速構建分布式系統中一些常見模式的工具(例如配置管理,服務發現,斷路器,智...
    卡卡羅2017閱讀 134,781評論 18 139
  • 博客原文 徒手翻譯spring framework 4.2.3官方文檔的第33章,若有翻譯不當之處請指正。 定時任...
    rabbitGYK閱讀 5,657評論 4 24
  • 西藏的天總藍的能滌蕩人的靈魂,我愿意把人生最美的時光獻給這片土地。 圖片均為本人拍攝,轉載請注明出處。
    AndrewHu2106閱讀 412評論 0 2
  • 我的多肉養殖環境:家里院子,通風,6小時日照(不垂直) 怕水多了的 —勞爾老樁 —墨西哥巨人 —熊童子 -冰莓 —...
    乳糖不耐的Apple閱讀 430評論 0 0