Django 即時聊天:channels 與 channels-redis 新手指南

更新日期: 2024 年 12 月 11 日

在傳統的 Django 應用中,伺服器只負責處理用戶的請求並返回結果,這是一種「一問一答」的模式。

然而,即時聊天需要伺服器,隨時將用戶的訊息傳遞給其他用戶,這與傳統模式不同。

為了解決這個問題,Django 提供了 channelschannels-redis,兩者相輔相成,讓 Django 能夠實現即時通訊功能。


channels 是什麼?

channels 是 Django 的擴展工具,專門用來處理即時訊息。

例如,傳統的 HTTP 協議需要用戶主動發送請求,但即時聊天需要伺服器,能夠主動將訊息推送給其他用戶,這就需要用到 WebSocket 技術,而 channels 正是為了支援這種技術而設計的。

舉個例子:
當用戶 A 在聊天室中發送一條訊息時,伺服器可以立刻通過 channels 將這條訊息傳送給聊天室中的所有其他用戶。


channels-redis 又是什麼?

channels-redischannels 的後端實現,用來管理 Channel Layer,它基於 Redis(一種高速的資料庫)來處理訊息的存儲與分發。

你可以把 channels-redis 想像成一個「訊息中轉站」,所有的訊息都會先放到 Redis 中,然後再由伺服器負責分發給正確的用戶。

比喻說明:

  • channels 是負責送信的郵差。
  • channels-redis 是信件暫存的郵局信箱,郵差會從這裡取信並送到收件人手上。

為什麼需要同時使用 channelschannels-redis

在實現即時聊天功能時,channelschannels-redis 各自負責不同的部分:

  1. channels
    • 負責處理 WebSocket,實現伺服器與用戶之間的即時雙向溝通。
    • 管理訊息的路由,確保每條訊息被正確處理。
  2. channels-redis
    • 負責訊息的存儲與分發,尤其是在多伺服器或多進程的環境中,確保訊息能正確傳遞。
    • 使用 Redis 的發布/訂閱(Pub/Sub)功能,高效處理跨用戶和跨伺服器的訊息同步。

即時聊天的實現流程(白話版)

以下是即時聊天功能的簡化工作流程:

  1. 用戶 A 發送訊息
    用戶 A 在聊天室中輸入:「嗨!有空嗎?」並按下發送。
  2. 伺服器接收訊息
    Django 通過 channels 接收到這條訊息,並將訊息轉換成事件。
  3. 訊息存入 Redis
    channels-redis 將這條訊息存入 Redis,等待分發。
  4. 伺服器分發訊息
    伺服器通過 Redis 的發布/訂閱機制,將訊息分發給所有應該收到的人。
  5. 用戶 B 接收訊息
    用戶 B 的瀏覽器接收到伺服器傳來的訊息,畫面上立即顯示:「嗨!有空嗎?」

配置範例

實現即時聊天需要安裝並配置 channelschannels-redis。以下是基本的設定步驟:

安裝所需工具

pip install channels channels-redis

配置 ASGI 應用(asgi.py

在 Django 專案的 asgi.py 中,設置 WebSocket 路由:

from channels.routing import ProtocolTypeRouter, URLRouter
from channels.auth import AuthMiddlewareStack
from chats.routing import websocket_urlpatterns

application = ProtocolTypeRouter({
    "http": get_asgi_application(),
    "websocket": AuthMiddlewareStack(
        URLRouter(
            websocket_urlpatterns
        )
    ),
})

配置 Channel Layer(settings.py

將 Redis 作為 Channel Layer 的後端:

CHANNEL_LAYERS = {
    "default": {
        "BACKEND": "channels_redis.core.RedisChannelLayer",
        "CONFIG": {
            "hosts": [("127.0.0.1", 6379)],  # Redis 伺服器地址
        },
    },
}

定義 Consumer(consumers.py

consumers.py 中創建一個處理 WebSocket 的消費者:

from channels.generic.websocket import AsyncWebSocketConsumer
import json

class ChatConsumer(AsyncWebSocketConsumer):
    async def connect(self):
        self.room_name = "chat_room"
        await self.channel_layer.group_add(
            self.room_name,
            self.channel_name
        )
        await self.accept()

    async def disconnect(self, close_code):
        await self.channel_layer.group_discard(
            self.room_name,
            self.channel_name
        )

    async def receive(self, text_data):
        message = json.loads(text_data).get("message")
        await self.channel_layer.group_send(
            self.room_name,
            {
                "type": "chat_message",
                "message": message
            }
        )

    async def chat_message(self, event):
        await self.send(text_data=json.dumps({
            "message": event["message"]
        }))

定義 WebSocket 路由(routing.py

routing.py 中設置 WebSocket 的路由:

from django.urls import path
from . import consumers

websocket_urlpatterns = [
    path("ws/chat/", consumers.ChatConsumer.as_asgi()),
]

總結

  • channels 是讓 Django 支援即時功能的核心工具,負責管理 WebSocket 的連接和消息的路由。
  • channels-redischannels 的夥伴,用來存儲和分發訊息,特別是在多伺服器環境下表現出色。

透過這兩個工具的配合,你可以輕鬆在 Django 中實現即時聊天、即時通知或多人互動的應用功能,為用戶帶來更好的體驗!

Similar Posts