任務隊列/任務調度:Celery和APScheduler

Celery VS APScheduler:

celery: celery是一個專注於實時處理和任務調度的任務隊列,任務就是消息(消息隊列使用rabbitmq或者redie),消息中的有效載荷中包含要執行任務的全部數據。我們通常將celery作爲一個任務隊列來使用,但是celery也有定時任務的功能,但是celery無法在flask這樣的系統中動態的添加定時任務,而且單獨爲定時任務功能而搭建celery顯得過於重量級。

apscheduler: apscheduler是基於Quartz的一個Python定時任務框架,提供了基於日期、固定時間間隔以及crontab類型的任務,並且可以持久化作業。APScheduler算是在實際項目中最好用的一個工具庫,不僅可以在程序中動態的添加和刪除定時任務,還支持持久化




消息隊列(redis,rabbitmq)

上面的兩者都可以看做是任務隊列,任務隊列是邏輯模型,消息隊列是通信模型
任務隊列:將抽象的任務發送到執行的worker的組件,用於處理任務。
消息隊列MQ:是一種能實現生產者到消費者的單向通信模型,用於通知,傳遞消息。
任務隊列可以需要消息隊列來實現,比如redis作爲celery的broker。




APScheduler

首先安裝pip install apscheduler

APScheduler有四個組件:

  1. triggers: 觸發器,用於設定觸發任務的條件,觸發器包含了調度的邏輯,每個任務都有自己的觸發器決定該任務下次運行的時間。
  2. job stores: 任務儲存器,用於存放任務,把任務放在內存或者數據庫中,一個
  3. executors: 執行器,用於執行任務,可以設定執行模式爲單線程或者線程池,任務完畢後,執行器會通知調度器
  4. schedulers: 調度器,上面的三個組件都是參數,使用這三個參數創建調度器實例來運行

調度器的選擇:

根據不同的開發需求,選擇對應的調度器組件

  1. BlockingScheduler 阻塞式調度器:適用於只跑調度器的程序。
  2. BackgroundScheduler 後臺調度器:適用於非阻塞的情況,調度器會在後臺獨立運行。
  3. AsyncIOScheduler AsyncIO調度器,適用於應用使用AsnycIO的情況。
  4. GeventScheduler Gevent調度器,適用於應用通過Gevent的情況。
  5. TornadoScheduler Tornado調度器,適用於構建Tornado應用。
  6. TwistedScheduler Twisted調度器,適用於構建Twisted應用。
  7. QtScheduler Qt調度器,適用於構建Qt應用。

任務儲存器的選擇:

如果運行的任務是無狀態的,選擇默認的任務儲存器MemoryJobStore即可。

如果需要程序關閉或者重啓的時候,保存任務的狀態,那麼需要持久化的任務儲存器比如SQLAlchemyJobStore配合postgres作爲後臺數據庫。


執行器的選擇:

默認的ThreadPoolExecutor線程池執行器方案可以滿足大部分需求
如果程序是計算密集型,推薦使用ProcessPoolExecutor進程池執行器使用多核能力,還可以混合使用兩種


配置任務觸發器:

一共有三種內置的觸發器:

  1. date 日期,觸發任務運行的具體時間
  2. interval 間隔,觸發任務運行的時間間隔
  3. cron 週期,觸發任務運行的週期,較複雜,check it in google
from datetime import date
from apscheduler.schedulers.blocking import BlockingScheduler

sched = BlockingScheduler()

def my_job(text):
    print(text)

# date觸發器
sched.add_job(my_job, 'date', run_date=date(2009, 11, 6), args=['text'])
# interval觸發器
sched.add_job(job_function, 'interval', hours=2)


sched.start()

對於cron,是一個強大的類crontab表達式:

# 任務會在6月、7月、8月、11月和12月的第三個週五,00:00、01:00、02:00和03:00觸發
sched.add_job(job_function, 'cron', month='6-8,11-12', day='3rd fri', hour='0-3')


現在創建一個調度器例子:

from apscheduler.schedulers.background import BackgroundScheduler

scheduler = BackgroundScheduler()

# 因爲是非阻塞的後臺調度器,所以程序會繼續向下執行

這樣就可以創建了一個後臺調度器。這個調度器有一個名稱爲default的MemoryJobStore(內存任務儲存器)和一個名稱是default且最大線程是10的ThreadPoolExecutor(線程池執行器)。

然後我們創建一個更復雜的調度器:

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(), # 名字爲mongo的MongoDBJobStore
    'default': SQLAlchemyJobStore(url='sqlite:///jobs.sqlite') # 名字爲default的任務儲存器
}
executors = {
    'default': ThreadPoolExecutor(20), # 最大線程20的線程池
    'processpool': ProcessPoolExecutor(5)
}
job_defaults = {
    'coalesce': False,  # 默認爲新任務關閉合並模式
    'max_instances': 3  # 設置新任務默認最大實例數爲3
}
scheduler = BackgroundScheduler(jobstores=jobstores, executors=executors, job_defaults=job_defaults, timezone=utc)

啓動調度器:
啓動只需要調用start(),非阻塞調度器都會立即返回,可以繼續運行之後的代碼,比如添加任務。對於阻塞調度器BlockingScheduler,程序會阻塞在start的位置。並且,調度器啓動後就不能修改配置了。

添加任務:
add_job()或者通過裝飾器scheduled_job(),需要注意的是,如果是從數據庫讀取任務,那麼必須爲每一個任務定義一個明確的ID,並且使用replace_existing=True屬性,否則每次重啓程序的時候,都會得到一份新的任務拷貝,因爲任務的狀態不會被保存。

移除任務:
remove_job()參數爲任務ID或者任務儲存器名字,或者在通過add_job()創建的任務實例上調用remove()方法。第二種方法更好,但是必須是創建任務實例的時候被保存在變量中,如果使用裝飾器創建的任務,那麼只能選擇第一種方法。

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

暫停和恢復任務:
暫停:
apscheduler.job.Job.pause()
apscheduler.schedulers.base.BaseScheduler.pause_job()
恢復:
apscheduler.job.Job.resume()
apscheduler.schedulers.base.BaseScheduler.resume_job()

獲取任務列表:
通過get_jobs()就可以獲得一個可修改的任務列表。get_jobs()第二個參數可以指定任務儲存器名稱,那麼就會獲得對應任務儲存器的任務列表。

修改任務:
通過apscheduler.job.Job.modify()或modify_job(),你可以修改任務當中除了id的任何屬性。

關閉調度器:
scheduler.shutdown()
scheduler.shutdown(wait=False)




Celery

rabbitmq和celery的區別 他們是兩個層面的東西,celery是一個分佈式的隊列,基本工作是管理分配任務到不同的服務器,但是服務器之間如何通信是celery不能解決的,所以rabbitmq作爲一個消息隊列管理工具被引入到和celery集成,現在crabbitmq在celery中扮演broker的角色,就是消息代理。

celery架構:
在這裏插入圖片描述
上面的異步任務模塊其實也是任務發佈者,所以產生任務的方式有兩種,一種是發佈者發佈任務(web應用), 另一種是任務調度按期發佈任務(定時任務)

使用的時候其實是從上到下的,broker是一個消息傳輸的中間件,每當程序調用celery異步任務的時候,會向broker發送消息,然後celery會監聽到這個消息,進行任務的執行,至於最下面的backend(數據庫,一般使用redis)用於儲存這些消息和celery執行的一些消息和結果。

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