Workers與背景任務 – Django Channels:實現 WebSocket 異步通信,打造高效的長連接系統

A. 簡介

雖然通道層主要是為了在不同的 ASGI 應用實例之間進行通信設計的,但它們也可以用於將工作卸載到一組聆聽固定通道名稱的工作伺服器上,作為一個簡單的、非常低延遲的任務隊列。

Channels 的Worker/背景任務系統簡單且非常快速,這是因為它缺少了一些可能對你有用的功能,例如重試機制或返回值。我們建議將其用於不需要完成保證的工作(最多一次傳遞)。而對於需要更多保證的工作,則考慮使用一個單獨的專用任務隊列。這個功能不適用於內存中的通道層。

設定背景任務分為兩個部分 – 發送事件,以及設置消費者以接收和處理事件。

B. 發送事件

要發送事件,只需將其發送到一個固定的通道名稱。例如,假設我們想要一個預先快取縮略圖的背景處理程序:

# Inside a consumer
self.channel_layer.send(
    "thumbnails-generate", # channel name
    {
        "type": "thumbnails.gen",
        "id": 123456789,
    },
)

請注意,您發送的事件必須包含一個 `type` 鍵,即使在通道上僅發送一種類型的訊息,因為這將轉變為消費者必須處理的事件。此外,請記住,如果您是在同步環境中發送事件,則必須使用 `asgiref.sync.async_to_sync` 包裝器,就如同在Channel Layers中指定的那樣。

C. 接收事件與編寫Consumer

Channels 會將傳入的工作者任務作為事件呈現給你,這些事件位於一個具有 `channel` 類型的範疇(scope)中,並且具有與通道名稱匹配的通道鍵。建議使用 `ProtocolTypeRouter` 和 `ChannelNameRouter`(參見Router部分以獲取更多信息)來安排你的消費者:

application = ProtocolTypeRouter({
    ...
    "channel": ChannelNameRouter({
        "thumbnails-generate": consumers.GenerateConsumer.as_asgi(),
    }),
})

當你發送事件時,你需要自行指定各個事件的類型值,所以需要決定你的命名並撰寫相應的消費者。舉個例子,以下是一個基礎的消費者,它預期處理類型為 thumbnails.gen 的事件,event接收事件資訊:

class GenerateConsumer(SyncConsumer):
    def thumbnails_gen(self, event):
        print(f"thumbnails generate for ", event['id'])

一旦你連接了消費者,你只需要運行一個處理它們的過程。由於這裡不涉及任何連接,因此無需使用協議伺服器,Channels 提供了 `runworker` 命令來達成這個目的。

先在在 INSTALLED_APPS 中加入channels,不然會出現 Unknown command: 'runworker' 的錯誤訊息,這個部分在官方文件並無提及,如果遇到可嘗試這個方法。

接著執行 runworker 指令:

python manage.py runworker thumbnails-generate

請注意,`runworker` 只會監聽您在命令列中傳遞給它的頻道。如果您未包含任何頻道,或忘記執行工作者,您的事件將無法被接收和處理。

D. Django Channels 的 Worker 與 Celery 的選擇時機

當在 Django 專案中,需要處理背景任務或非同步操作時,選擇合適的工具是至關重要的。Django Channels 和 Celery 是兩種常用的解決方案,但它們的應用場合有所不同,因此了解它們各自的優勢和使用場景可以幫助開發者做出最佳選擇。

1. Channels 的 Worker

Django Channels 提供了一種對 Django 應用進行非同步處理的方式,它是基於事件驅動的架構,主要針對 WebSocket 以及長連接等場景。Channels 的 Workers 可用來處理異步請求,管理多用戶連結,或處理需要即時響應的任務。

1-1. 適用場合

  1. 即時通訊:如聊天應用,即時遊戲,或即時通知。
  2. WebSocket 連接:任何需要長時間保持連接的應用。
  3. 需要即時響應的應用:如追蹤用戶活動或更新即時數據。

1-2. 優勢

  1. 與 Django 緊密集成,對於需要處理 WebSocket 的應用特別有用。
  2. 簡化了需要即時性和持久連接特性的應用開發。

2. Celery

Celery 是一個專注於分發和處理任務的背景作業系統,特別適合需要處理大量計算密集型任務或需要排程(如定時運行)的應用場景。Celery 對任務的排程和重試機制非常成熟,適合用於需要可靠執行的長任務。

2-1. 適用場合

  1. 計算密集型工作:例如生成報表,圖片處理,或數據分析。
  2. 定時任務:如夜間批量作業,或定時的數據同步。
  3. 大規模的後台數據處理:如電子郵件發送,或背景數據庫更新。

2-2. 優勢

  1. 支持多種資料庫後端(如 RabbitMQ, Redis)作為隊列管理。
  2. 健全的重試機制和狀態跟踪功能。

3. 選擇建議

如果應用需要: 

  • 即時、雙向通信或處理 WebSocket 相關的任務,建議選擇 Django Channels。
  • 長時間運行的、批量或計算密集型任務,建議選擇 Celery。

最終,根據專案的需求,可能還會考慮將這兩者結合使用。比如,使用 Channels 處理即時通信,同時讓 Celery 處理需要的長時間或定時的背景任務。這樣可以最大化利用每個工具的優勢,滿足各類型任務的性能需求。

E. 總結

在現代的 Django 專案中,異步處理和背景任務是越來越重要的需求,這使得選擇合適的工具來滿足不同的應用場景變得至關重要。透過 Django Channels 和 Celery,開發者擁有了處理即時性和排程任務的靈活選擇。

Django Channels 提供了一種處理非同步通信和長連接協議的方式,特別適合需要實時響應的應用程序,例如即時通訊、WebSocket 連接和多用戶互動的應用。Channels 簡便的 Worker 系統雖然缺乏一些高級功能(如重試機制),但它的低延遲特性使其成為即時應用的理想選擇。

另一方面,Celery 是一個專注於可靠任務調度和執行的背景作業系統,非常適合大規模的計算密集型任務或需要排程的操作。它強大的重試機制和支持多種資料庫後端(如 RabbitMQ、Redis),使其成為負責任和持久性後台任務的首選。

總結而言,若專案需要即時的雙向通信或與 WebSocket 相關的功能,Django Channels 是最佳選擇;而對於長時間運行的批量處理或計算密集型任務,Celery 則提供了更為專業的支持。根據具體需求,開發者還可以考慮將兩者結合使用,以充分利用每個工具的優勢來實現卓越的性能和效率。

F. 系列文章