什麼是Celery
celery是一個異步任務隊列/基於分佈式消息傳遞的作業隊列,分佈式隊列服務。它側重於實時操作,但對調度支持也很好。
celery用於生產系統每天處理數以百萬計的任務。
celery是用Python編寫的,但該協議可以在任何語言實現。它也可以與其他語言通過webhooks實現。
建議的消息代理RabbitMQ的,但提供有限支持Redis, Beanstalk, MongoDB, CouchDB, ,和數據庫(使用SQLAlchemy的或Django的 ORM) 。
celery是易於集成Django, Pylons and Flask,使用 django-celery, celery-pylons and Flask-Celery 附加包即可。
應用場景
- 異步任務
一些耗時較長的操作,如果用戶等待後臺數據返回,將會極大影響用戶體驗時,我們使用 異步消息隊列,就可以解決這個問題啦。前端可以迅速響應用戶請求,而一些異步操作則交給消息隊列去執行啦。比如發送短信/郵件、消息推送、音視頻處理等等。。 - 定時任務:
定時執行某件事情,比如每天數據統計
優惠券定期刪除
等等。。
Celery架構圖
- Producer:調用了Celery提供的API、函數或者裝飾器而產生任務並交給任務隊列處理的都是任務生產者。
- Celery Beat:任務調度器,Beat進程會讀取配置文件的內容,週期性地將配置中到期需要執行的任務發送給任務隊列。
- Broker:消息代理,又稱消息中間件,Celery本身不提供消息服務,但是可以方便的和第三方提供的消息中間件集成。 接受任務生產者發送過來的任務消息,存進隊列再按序分發給任務消費方(通常是消息隊列或者數據庫)。Celery目前支持RabbitMQ、Redis、MongoDB、Beanstalk、SQLAlchemy、Zookeeper等作爲消息代理,但適用於生產環境的只有RabbitMQ和Redis, 官方推薦 RabbitMQ。
- Celery Worker:執行任務的消費者,通常會在多臺服務器運行多個消費者來提高執行效率。
- Result Backend:任務處理完後保存狀態信息和結果,以供查詢。Celery默認已支持Redis、RabbitMQ、MongoDB、Django ORM、SQLAlchemy等方式。
實現
- 準備一個項目,結構如下。
- app.py : 實例化 Celery。
- config.py : Celery 相關配置。
- task.py : 任務函數文件。
- worker.py : 執行調用任務工作。
接下來依次看一下代碼。
app.py
# -*- coding: utf-8 -*-
__author__ = 'LiuNan'
__date__ = '2020/5/22 19:52'
from celery import Celery
# app是Celery類的實例,創建的時候添加了proj.tasks這個模塊,也就是包含了proj/tasks.py這個文件。
app = Celery('Celery_project', include=['Celery_project.task'])
app.config_from_object('Celery_project.config')
if __name__ == '__main__':
app.start()
config.py
# -*- coding: utf-8 -*-
__author__ = 'LiuNan'
__date__ = '2020/5/22 19:53'
BROKER_URL = 'redis://:yourpasswd@localhost' # 使用Redis作爲消息代理
CELERY_RESULT_BACKEND = 'redis://:yourpasswd@localhost:6379/0' # 把任務結果存在了Redis
CELERY_TASK_SERIALIZER = 'msgpack' # 任務序列化和反序列化使用msgpack方案
CELERY_RESULT_SERIALIZER = 'json' # 讀取任務結果一般性能要求不高,所以使用了可讀性更好的JSON
CELERY_TASK_RESULT_EXPIRES = 60 * 60 * 24 # 任務過期時間
CELERY_ACCEPT_CONTENT = ['json', 'msgpack'] # 指定接受的內容類型
task.py
# -*- coding: utf-8 -*-
__author__ = 'LiuNan'
__date__ = '2020/5/22 19:50'
import time
from Celery_project.app import app
@app.task
def add(x, y):
time.sleep(5) # 模擬執行時間5秒
return x + y
worker.py
# -*- coding: utf-8 -*-
__author__ = 'LiuNan'
__date__ = '2020/5/22 19:56'
import time
# from Celery_project.task import add
import sys
sys.path.append(r'/root/')
from Celery_project.task import add
t1 = time.time()
# 用 delay() 方法來調用任務
# 調用任務會返回一個 AsyncResult 實例,可用於檢查任務的狀態,等待任務完成或獲取返回值(如果任務失敗,則爲異常和回溯)。
r1 = add.delay(1, 2)
r2 = add.delay(3, 2)
r3 = add.delay(7, 2)
r4 = add.delay(8, 2)
r5 = add.delay(10, 2)
r_list = [r1, r2, r3, r4, r5]
for r in r_list:
while not r.ready():
pass
print(r.result)
#
# print(add(1,2))
# print(add(3,2))
# print(add(7,2))
# print(add(8,2))
# print(add(10,2))
#
t2 = time.time()
print('耗時%s' % str(t2 - t1))
運行
-
啓動redis
-
切換至所在目錄,執行
celery -A Celery_project.app worker -l info
運行結果圖,如下。
-
運行調用任務文件 worker.py
可以看出, 程序總共執行了 5 秒.
我定義任務執行時間 是5秒, 如果是同步執行, 我執行了5次, 那麼最少需要 25 秒.
所以可以看出celery 的作用.