Celery雜談

在本篇文章中不會針對 celery 的某個部分話題進行深刻介紹,而是記錄一些學習和使用 celery 的過程當中讓我困惑的一些問題。mysql


1、celery 作了什麼

celery 官方文檔介紹說 celery 是一個分佈式任務隊列,可是經過閱讀其官方文檔很難讓開發者感覺到是分佈式的,由於官方並無提供分佈式的代碼樣例以及相關說明。
celery 是封裝了消息隊列,好比經常使用的 rabbitmq、kafka或者nysql、redis等等這種僞消息隊列,對mysql、redis用相似 rabbitmq 和 kafak 中的概念進行了抽象,其實 celery 也沒有本身去針對這些消息隊列去進行抽象,而是大量的藉助了 kombu 的代碼, kombu 底層纔是真正的將 消息隊列給抽象了起來,那 celery 爲何又封裝了一層?celery 實際上是將消息隊列中的生產者和消費者的使用方式進行了抽象,kombu 是將消息隊列的使用方式進行了抽象。在 celery 的世界裏能夠忽略生產者和消費者概念,只考慮任務的概念,celery 幫助開發者將要執行的函數封裝成了任務,這個任務一調用就會在本地機器執行或者在遠程機器執行。這樣開發者就能夠專心的寫業務邏輯了。redis


2、celery 中的任務是如何執行的

@app.task
def add(x, y):
    return x+y

咱們使用時都會像上面那段代碼同樣,將咱們的函數用裝飾器包裝起來造成一個任務,這個任務當咱們調用 add.delay(1,2) 時,其實調用了消息隊列中的生產者,根據 celery 的配置文件,或者在裝飾器中傳遞的參數會選擇路由和隊列從而將任務發送給消費者。消費者獲取到任務就會將任務扔到進程池、線程池、協程中的某一個策略中去運行任務。
生產者發送的必定是任務的元數據信息,確定不會發送一個對象的序列化格式過去,不是不能夠,可是不建議那樣作。元數據最重要的是任務的 id 以及 name。消費和會根據任務名稱去尋找要執行的任務,消費者是如何根據任務名稱找到將要執行的任務的,看下一小節。sql


3、celery 消費者如何根據任務名稱找到要執行的任務

celery 中寫好的任務文件不單單要在生產者(分發任務的機器) 上要存在,在消費者(啓動 worker)d的機器上也要存在,這樣任務才能註冊到數據結構中,worker 收到任務的元數據纔會找到要執行的任務,這一點官方文檔沒有樣例,也沒有明確的說明,因此很讓人不解 celery是怎麼個分佈式的,要讓 worker(消費者)在啓動時可以將全部任務註冊進來,有四種實現方式。
1)將 Celery 的實例與全部的 task 寫在同一個文件中數據結構

mkdir /root/project
cd /root/project
touch __init__.py
touch celery.py

cat celery.py
from celery import Celery
app = Celery(include=["project.tasks"])

touch tasks.py
cat tasks.py
from project.celery import app

@app.task
def add(x,y):
    reurn x+y
   
@app.task
def sub(x,y):
    return x-y

這樣在啓動 worker 時要制定項目中 Celery 實例所在的路徑,這個時候裝飾器會執行,同時會將任務註冊,可是這種方法在平時簡單寫寫腳本還好,若是在工程化項目中就顯得太混亂,沒有分類,全部任務都在一個文件中,不清晰沒有層次感。
2)命令行
在 worker 啓動時有個 -I 的參數選項,這個選項能夠以逗號進行分割傳入一個或者多個任務文件路徑,好比有兩個文件存在任務
3) 在執行 Celery 類實例化時做爲參數傳入app

app = Celery(include=["project.tasks"])

這種方式比較推薦
4) 在配置文件中指定分佈式

在 celery 的用戶配置文件中有項 CELERY_IMPORTS = ("project.tasks",) 推薦使用這種方式,比較靈活,方便維護

將 celery 的實例和 相關的 tasks 都寫入到一個文件是最不推薦在項目中使用的,若是是簡單的工具沒有問題,經過命令行的傳入也不是很優雅,3和4種不管採用哪一種都須要在分發任務和全部 worker 機器上進行一致性維護,因此仍是採用配置文件的方式比較好,當文件修改後統一下發到各個機器上。函數

相關文章
相關標籤/搜索