Python定時任務對比-schedule & Celery & APScheduler

總結

使用定時任務,開源庫或自寫一個。
比較簡單的方式是:

while 1:
       time.sleep(n)
       do()

或是協程方式

while 1:
     asyncio.sleep(n)
    do()

celery apscheduler schedule 對比

從順序可以看出,一個比一個輕量級。

  • celery 是經過生產級考量,但遇到問題,排查時候,比較坑,它的優勢重在異步隊列,雖也可用在定時任務。

  • apscheduler 專注于定時任務,功能豐定,文檔寫得還算全,但沒有對各個場景的詳細說明。一些具體用法,還得去源代碼,怎么調用,比如redis作為store

  • schedule 類似于linux的cron,簡單好用。

使用schedule庫

schedule庫是一個輕量級的定時任務方案,優勢是使用簡單,也不需要做什么配置;缺點是無法動態添加任務,也無法將任務持久化。

安裝
pip install schedule

使用
import schedule
import time

def job():
    print("I'm working...")

schedule.every(10).minutes.do(job)
schedule.every().hour.do(job)
schedule.every().day.at("10:30").do(job)
schedule.every(5).to(10).minutes.do(job)
schedule.every().monday.do(job)
schedule.every().wednesday.at("13:15").do(job)

while True:
    schedule.run_pending()
    time.sleep(1)

使用Celery

Celery在Python領域可謂大名鼎鼎,我們通常將Celery作為一個任務隊列來使用,不過Celery也同時提供了定時任務功能。通常,當我們的解決方案中已經在使用Celery的時候可以考慮同時使用其定時任務功能,但是Celery無法在Flask這樣的系統中動態添加定時任務(在Django中有相應的插件可以實現動態添加任務),而且如果對于不使用Celery的項目,單獨為定時任務搭建Celery顯得過于重量級了。(搭建Celery比較麻煩,還需要配置諸如RabbitMQ之類消息分發程序)。

Celery安裝在此不再贅述,大家可以參考官網的資料

使用

Celery雖然無法動態添加定時任務,但是可以在程序固定位置添加定時任務,如下:

from celery import Celery
from celery.schedules import crontab

app = Celery()

# 此處on_after_configure裝飾符意味著當Celery app配置完成之后調用該hook函數
@app.on_after_configure.connect
def setup_periodic_tasks(sender, **kwargs):
    # Calls test('hello') every 10 seconds.
    sender.add_periodic_task(10.0, test.s('hello'), name='add every 10')

    # Calls test('world') every 30 seconds
    sender.add_periodic_task(30.0, test.s('world'), expires=10)

    # Executes every Monday morning at 7:30 a.m.
    sender.add_periodic_task(
        crontab(hour=7, minute=30, day_of_week=1),
        test.s('Happy Mondays!'),
    )

@app.task
def test(arg):
    print(arg)

  • 這里調用add_periodic_task用于添加一個定時任務,相當于在Celery config文件中的beat_schedule設置項中添加了一項,如下:

    app.conf.beat_schedule = {
        'add-every-30-seconds': {
            'task': 'tasks.add',
            'schedule': 30.0,
            'args': (16, 16)
        },
    }
    
    
  • add_periodic_task中指定job function時需要用.s()來調用

使用APScheduler

筆者認為APScheduler是在實際項目最好用的一個工具庫。它不僅可以讓我們在程序中動態添加和刪除我們的定時任務,還支持持久化,且其持久化方案支持很多形式,包括(Memory, MongoDB, SQLAlchemy, Redis, RethinkDB, ZooKeeper), 也可以非常好與一些Python framework集成(包括asyncio, gevent, Tornado, Twisted, Qt). 筆者所在的項目使用的是Flask框架,也有相應的插件可以供我們直接使用。

但是筆者沒有使用插件,而是直接將APScheduler集成于項目代碼中。

初始化scheduler
# 可以在初始化Flask的時候調用,并將返回的scheduler賦給app
def init_scheduler():
    # 這里用于持久化的設置,代碼中演示使用MongoDB
    # client用于設置你自己的MongoDB的handler, 即MongoClient對象
    jobstores = {
        'default': MongoDBJobStore(client=your_db_handler, collection="schedule_job")
    }
    executors = {
        'default': ThreadPoolExecutor(20)
    }
    job_defaults = {
        'coalesce': False,
        'max_instances': 5
    }
    # 這里使用BackgroundScheduler即可
    scheduler = BackgroundScheduler(jobstores=jobstores, executors=executors, job_defaults=job_defaults, timezone=utc)
    # 注意這里一定要調用start啟動scheduler
    scheduler.start()
    return scheduler

添加定時任務

APScheduler將定時任務分為三種:

  • interval: 比如每隔5分鐘執行一次任務
  • cron: 比如每天早上5點執行一次任務
  • date: 比如在2018年5月5日執行一次任務

我們以添加cron job為例:

def test_job(name):
    print "hello, %s" % name

def add_daily_job(name):
    exec_time = datetime.now() + timedelta(minutes=2)
    hour = exec_time.strftime("%H")
    minute = exec_time.strftime("%M")
    # 這里要選擇'cron'
    # 另外,job_id可以根據你自己的情況設定,其會被用于remove_job
    current_app.scheduler.add_job(
        test_job, 'cron', hour=hour, minute=minute,
        args=[name], id=job_id)

刪除定時任務

通過在add_job時使用的job_id可以刪除對應的定時任務。實際上在我們添加任務的時候,APScheduler會把相應的任務信息存儲于我們jobstore中設置的持久化存儲方案,這里使用的是MongoDB,然后當刪除的時候會將相應的任務從MongoDB中刪除。

def remove_daily_job(job_id):
    current_app.scheduler.remove_job(job_id)

總結:

APScheduler在實際使用過程中擁有最大的靈活性,可以滿足我們的大部分定時任務的相關需求;Celery比較重量級,通常如果項目中已有Celery在使用,而且不需要動態添加定時任務時可以考慮使用;schedule非常輕量級,使用簡單,但是不支持任務的持久化,也無法動態添加刪除任務,所以主要用于簡單的小型應用。

References

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

推薦閱讀更多精彩內容