一、任務隊列(Task Queues)

1.1 什么是任務隊列?
任務隊列用於管理后台工作,通常這些后台工作必須在 HTTP請求-響應循環 之外執行。
1.2 為什么需要任務隊列?
對於那些不是由客戶端HTTP請求產生的任務,或是需要長時間運行的作業,會大大降低HTTP響應的性能,所以這些請求需要異步處理。
示例一:一個Web應用程序可以每10分鍾輪詢一次GitHub API,以收集前100個加星標存儲庫的名稱。任務隊列將處理調用代碼以調用GitHub API、處理結果、並將其存儲在持久數據庫中以供以后使用。
示例二:在HTTP請求-響應周期內數據庫查詢花費的時間太長時。查詢可以在后台以固定間隔執行,結果存儲在數據庫中。當收到一個需要這些結果的HTTP請求時,查詢將簡單地獲取預先計算的結果,而不是重新執行較長的查詢。這種預計算方案是任務隊列啟用的一種緩存形式。
任務隊列的其他類型的作業包括
-
隨着時間的流逝散布大量獨立的數據庫插入,而不是一次插入所有內容
-
以固定的時間間隔(例如每15分鍾)聚合收集的數據值
-
安排諸如批處理之類的定期作業
1.2 常用的任務隊列
事實上的標准Python任務隊列是Celery。其他任務隊列項目的出現,主要是因為對於簡單的使用場景而言Clelery還是有點繁瑣。所以重點還是在Celery。
此外還可以使用第三方的任務隊列服務,用於解決在大規模部署分布式任務隊列時出現的復雜問題。

二、Celery架構及其工作原理概述
運行異步任務的問題可以輕松地映射到經典的生產者/消費者問題問題。生產者將作業排在隊列中。消費者然后檢查隊列中的頭以等待作業,選擇第一個並執行。

在上面的異步任務隊列中,生產者通常是web節點或其他發放任務的系統,隊列稱為broker,消費者稱為workers。而workers也可以發布任務到隊列中,這時候它就成了“生產者”。現在我們已經對任務隊列有一個概覽了,接下來再挖深一點。
2.1 Broker
broker也常譯為中間人,其實就是一個隊列。但是在計算機系統中實現一個隊列的方法有很多。最簡單的方式就是使用文本文件。文本文件可以包含要執行的一系列工作描述,因此,我們可以將它們用作系統的代理。文本文件的問題在於它們不能處理實際的應用程序問題,例如網絡和並發訪問。因此,我們需要更強大的功能。
另一方面,SQL數據庫能夠在網絡中運行並處理並發訪問。它們的問題在於它們太慢。相比之下,NoSQL數據庫的速度相當快,但是很多時候它們缺乏可靠性。
因此,在構建隊列時,我們應該使用快速,可靠,並發啟用的工具,例如RabbitMQ,Redis和SQS。
Celery完全支持RabbitMQ和Redis。盡管也可以使用SQS和Zookeeper,但它們提供的功能有限。
2.2 Web節點 和 Workers
web節點和workers都是普通的服務,他們的不同僅僅是:web節點接收客戶端請求,然后發布需要異步處理的任務;而workers服務接收這些任務,執行並提供反饋。
盡管他們的執行邏輯不同,但一般這兩者的代碼都放在同一個項目倉庫中,這樣做兩個應用程序都可以受益於共享模型和服務之類的東西,還可以防止這些模型和服務不一致。
2.3 執行一個異步任務
# worker node:
from celery import Celery
app = Celery(...)
@app.task
def add(a, b):
return a + b
# web node:
from tasks import add
r = add.delay(4, 5).get()
print(r) # 9
上面分別是 異步任務需要運行的代碼 和 將作業放置在要運行的隊列中的代碼。在此示例中,Web節點放置一個作業,並等待直到結果可用。響應的結果到來后,將打印結果。
2.4 Results Backend
在前面的示例中,我們將一起調用delay
和get
函數。實際上,它們是兩個獨立的事物。delay
將任務放在隊列中並返回一個promise,該promise可用於監視狀態並在准備就緒時獲取結果。調用get
該promise將阻塞執行(block the execution),直到結果可用為止。
這個add
任務必須將結果存儲在某個位置,以便隨后觸發它的進程可以對其進行訪問。這意味着我們錯過了一部分架構。除了web
,broker
和worker
組件,還有一個results backend
。

Results Backend 用於存儲任務結果。其實也可以使用另一個broker來存儲結果。除了受支持的代理選項以外,還有其他技術可以用作Celery中的Results Backend,但是根據使用的內容,會有一些差異。例如,在Postgres中,該get
方法將進行輪詢以檢查結果是否准備就緒。對於其他工具,例如Redis,則是通過pub / sub協議完成的。