Django 即時聊天:channels 與 channels-redis 新手指南
更新日期: 2024 年 12 月 11 日
在傳統的 Django 應用中,伺服器只負責處理用戶的請求並返回結果,這是一種「一問一答」的模式。
然而,即時聊天需要伺服器,隨時將用戶的訊息傳遞給其他用戶,這與傳統模式不同。
為了解決這個問題,Django 提供了 channels
和 channels-redis
,兩者相輔相成,讓 Django 能夠實現即時通訊功能。
channels
是什麼?
channels
是 Django 的擴展工具,專門用來處理即時訊息。
例如,傳統的 HTTP 協議需要用戶主動發送請求,但即時聊天需要伺服器,能夠主動將訊息推送給其他用戶,這就需要用到 WebSocket 技術,而 channels
正是為了支援這種技術而設計的。
舉個例子:
當用戶 A 在聊天室中發送一條訊息時,伺服器可以立刻通過 channels
將這條訊息傳送給聊天室中的所有其他用戶。
channels-redis
又是什麼?
channels-redis
是 channels
的後端實現,用來管理 Channel Layer,它基於 Redis(一種高速的資料庫)來處理訊息的存儲與分發。
你可以把 channels-redis
想像成一個「訊息中轉站」,所有的訊息都會先放到 Redis 中,然後再由伺服器負責分發給正確的用戶。
比喻說明:
channels
是負責送信的郵差。channels-redis
是信件暫存的郵局信箱,郵差會從這裡取信並送到收件人手上。
為什麼需要同時使用 channels
和 channels-redis
?
在實現即時聊天功能時,channels
和 channels-redis
各自負責不同的部分:
channels
:- 負責處理 WebSocket,實現伺服器與用戶之間的即時雙向溝通。
- 管理訊息的路由,確保每條訊息被正確處理。
channels-redis
:- 負責訊息的存儲與分發,尤其是在多伺服器或多進程的環境中,確保訊息能正確傳遞。
- 使用 Redis 的發布/訂閱(Pub/Sub)功能,高效處理跨用戶和跨伺服器的訊息同步。
即時聊天的實現流程(白話版)
以下是即時聊天功能的簡化工作流程:
- 用戶 A 發送訊息
用戶 A 在聊天室中輸入:「嗨!有空嗎?」並按下發送。 - 伺服器接收訊息
Django 通過channels
接收到這條訊息,並將訊息轉換成事件。 - 訊息存入 Redis
channels-redis
將這條訊息存入 Redis,等待分發。 - 伺服器分發訊息
伺服器通過 Redis 的發布/訂閱機制,將訊息分發給所有應該收到的人。 - 用戶 B 接收訊息
用戶 B 的瀏覽器接收到伺服器傳來的訊息,畫面上立即顯示:「嗨!有空嗎?」
配置範例
實現即時聊天需要安裝並配置 channels
和 channels-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-redis
是channels
的夥伴,用來存儲和分發訊息,特別是在多伺服器環境下表現出色。
透過這兩個工具的配合,你可以輕鬆在 Django 中實現即時聊天、即時通知或多人互動的應用功能,為用戶帶來更好的體驗!