該篇着重介紹一下如何調用任務,隊列路由。詳見Celery中文手冊
一、在項目中使用Celery
-
我的項目目錄:
TestCelery/ ├── proj │ ├── celeryconfig.py │ ├── celery.py │ ├── init.py │ └── tasks.py └── test.py
-
celery.py文件內容如下:
from celery import Celery # 創建celery實例 app = Celery('demo') app.config_from_object('proj.celeryconfig') # 自動搜索任務 app.autodiscover_tasks(['proj'])
-
celeryconfig.p模塊內容如下:
from kombu import Exchange, Queue BROKER_URL = 'redis://:[email protected]:6379/1' CELERY_RESULT_BACKEND = 'redis://:[email protected]:6379/2'
-
tasks.py模塊內容如下:
from proj.celery import app as celery_app # 創建任務函數 @celery_app.task def my_task1(): print("任務函數(my_task1)正在執行....") @celery_app.task def my_task2(): print("任務函數(my_task2)正在執行....") @celery_app.task def my_task3(): print("任務函數(my_task3)正在執行....")
-
啓動worker:如圖
celery -A proj worker -l info
在終端按 ctrl+c 即可關閉worker
二、調用任務Calling Task
-
調用任務,最簡單的方法就是使用delay()方法:
my_task.delay(2, 2)
-
也可以使用apply_async()方法,該方法可讓我們設置一些任務執行的參數,例如,任務多久之後才執行,任務被髮送到那個隊列中等等.
my_task.apply_async((2, 2), queue='my_queue', countdown=10)
任務my_task將會被髮送到my_queue隊列中,並且在發送10秒之後執行。
-
如果我們直接執行任務函數,將會直接執行此函數在當前進程中,並不會向broker發送任何消息。
-
無論是
delay()
還是apply_async()
方式都會返回AsyncResult對象,方便跟蹤任務執行狀態,但需要我們配置 result_backend. -
每一個被吊用的任務都會被分配一個ID,我們叫Task ID
-
三、Designing Work-flows
-
signature
我們到目前爲止只是學習瞭如何使用delay()方法,當然這個方法也是非常常用的。但是有時我們並不想簡單的將任務發送到隊列中,我們想將一個任務函數(由參數和執行選項組成)作爲一個參數傳遞給另外一個函數中,爲了實現此目標,Celery使用一種叫做signatures的東西。一個signature包裝了一個參數和執行選項的單個任務調用。我們可將這個signature傳遞給函數。
我們先看下tasks.py模塊中定義的任務函數:
from proj.celery import app as celery_app # 創建任務函數 @celery_app.task def my_task1(): print("任務函數(my_task1)正在執行....") @celery_app.task def my_task2(): print("任務函數(my_task2)正在執行....") @celery_app.task def my_task3(): print("任務函數(my_task3)正在執行....")
我們將my_task1()任務包裝稱一個signature:
t1 = my_task1.signatures(countdown=10) t1.delay()
-
Primitives
這些primitives本身就是signature對象,因此它們可以以多種方式組合成複雜的工作流程。primitives如下:- group: 一組任務並行執行,返回一組返回值,並可以按順序檢索返回值。
- chain: 任務一個一個執行,一個執行完將執行return結果傳遞給下一個任務函數.
tasks.py模塊如下:
from proj.celery import app as celery_app # 創建任務函數 @celery_app.task def my_task1(a, b): print("任務函數(my_task1)正在執行....") return a + b @celery_app.task def my_task2(a, b): print("任務函數(my_task2)正在執行....") return a + b @celery_app.task def my_task3(a, b): print("任務函數(my_task3)正在執行....") return a + b
group案例如下(test.py模塊):
from proj.tasks import my_task1 from proj.tasks import my_task2 from proj.tasks import my_task3 from celery import group # 將多個signature放入同一組中 my_group = group((my_task1.s(10, 10), my_task2.s(20, 20), my_task3.s(30, 30))) ret = my_group() # 執行組任務 print(ret.get()) # 輸出每個任務結果
chain案例如下(test.py模塊):
from proj.tasks import my_task1 from proj.tasks import my_task2 from proj.tasks import my_task3 from celery import chain # 將多個signature組成一個任務鏈 # my_task1的運行結果將會傳遞給my_task2 # my_task2的運行結果會傳遞給my_task3 my_chain = chain(my_task1.s(10, 10) | my_task2.s(20) | my_task3.s(30)) ret = my_chain() # 執行任務鏈 print(ret.get()) # 輸出最終結果
四、路由Routing
-
問題:
假如我們有兩個worker、一個worker專門用來處理郵件發送任務和圖像處理任務,一個worker專門用來處理文件上傳任務。 -
解決方案:
我們創建兩個隊列,一個專門用於存儲郵件任務隊列和圖像處理,一個用來存儲文件上傳任務隊列。
Celery支持AMQP(Advanced Message Queue)所有的路由功能,我們也可以使用簡單的路由設置將指定的任務發送到指定的隊列中。 -
我們需要配置在
celeryconfig.py
模塊中配置CELERY_ROUTES
項;
tasks.py模塊修改如下:from proj.celery import app as celery_app @celery_app.task def my_task1(a, b): print("my_task1任務正在執行....") return a + b @celery_app.task def my_task2(a, b): print("my_task2任務正在執行....") return a + b @celery_app.task def my_task3(a, b): print("my_task3任務正在執行....") return a + b @celery_app.task def my_task4(a, b): print("my_task3任務正在執行....") return a + b @celery_app.task def my_task5(): print("my_task5任務正在執行....") @celery_app.task def my_task6(): print("my_task6任務正在執行....") @celery_app.task def my_task7(): print("my_task7任務正在執行....")
-
我們通過配置,將send_email和upload_file任務發送到queue1隊列中,將image_process發送到queue2隊列中。
我們修改celeryconfig.py:broker_url='redis://:@127.0.0.1:6379/1' result_backend='redis://:@127.0.0.1:6379/2' task_routes=({ 'proj.tasks.my_task5': {'queue': 'queue1'}, 'proj.tasks.my_task6': {'queue': 'queue1'}, 'proj.tasks.my_task7': {'queue': 'queue2'}, }, )
修改test.py如下:
from proj.tasks import * # 發送任務到路由指定的隊列中 my_task5.delay() my_task6.delay() my_task7.delay()
然後開啓兩個worker服務器,分別處理兩個隊列:
celery -A proj worker --loglevel=info -Q queue1 celery -A proj worker --loglevel=info -Q queue2
-
補充:
1、我們同樣也可以通過apply_aynsc()方法來設置任務發送到那個隊列中:my_task1.apply_async(queue='queue1')
2、我們也可設置一個worker服務器處理兩個隊列中的任務:
celery -A proj worker --loglevel=info -Q queue1,queue2