任務隊列 與 Celery概述

一、任務隊列(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協議完成的。


參考

Tags: