Bạn muốn hiển thị notification ngay khi có tin nhắn mới, không cần user reload trang. Có ba cách: polling (request mỗi giây), long polling (request và chờ), hoặc WebSocket (kết nối liên tục). WebSocket là giải pháp đúng cho real-time.
HTTP Vs WebSocket
# HTTP — Request/Response
Client: "Có tin nhắn mới không?" → Server: "Không"
Client: "Có tin nhắn mới không?" → Server: "Không"
Client: "Có tin nhắn mới không?" → Server: "Có! Đây..."
# → Polling tốn tài nguyên, latency cao
# WebSocket — Bidirectional
Client → Server: Handshake (HTTP upgrade)
Server ↔ Client: Kết nối hai chiều liên tục
Server → Client: "Bạn có tin nhắn mới!" # Server chủ động gửi
Django Channels — WebSocket Cho Django
pip install channels channels-redis
# settings.py
INSTALLED_APPS = [..., 'channels']
ASGI_APPLICATION = 'blog.asgi.application'
CHANNEL_LAYERS = {
'default': {
'BACKEND': 'channels_redis.core.RedisChannelLayer',
'CONFIG': {'hosts': [('localhost', 6379)]},
}
}
# blog/asgi.py
from channels.routing import ProtocolTypeRouter, URLRouter
from channels.auth import AuthMiddlewareStack
import notifications.routing
application = ProtocolTypeRouter({
'http': get_asgi_application(),
'websocket': AuthMiddlewareStack(
URLRouter(notifications.routing.websocket_urlpatterns)
),
})
WebSocket Consumer
import json
from channels.generic.websocket import AsyncWebsocketConsumer
class NotificationConsumer(AsyncWebsocketConsumer):
async def connect(self):
user = self.scope['user']
if not user.is_authenticated:
await self.close()
return
self.group_name = f'user_{user.id}'
# Tham gia group
await self.channel_layer.group_add(self.group_name, self.channel_name)
await self.accept()
async def disconnect(self, close_code):
await self.channel_layer.group_discard(self.group_name, self.channel_name)
# Nhận message từ client
async def receive(self, text_data):
data = json.loads(text_data)
await self.send(text_data=json.dumps({'echo': data}))
# Nhận message từ channel layer (group send)
async def send_notification(self, event):
await self.send(text_data=json.dumps({
'type': 'notification',
'message': event['message'],
}))
Gửi Notification Từ Anywhere
from channels.layers import get_channel_layer
from asgiref.sync import async_to_sync
def notify_user(user_id: int, message: str):
channel_layer = get_channel_layer()
async_to_sync(channel_layer.group_send)(
f'user_{user_id}',
{
'type': 'send_notification', # Method trong Consumer
'message': message,
}
)
# Dùng trong view hoặc Celery task
def create_comment(request, post_id):
comment = Comment.objects.create(...)
notify_user(post.author_id, f"{request.user.username} đã bình luận bài của bạn")
JavaScript Client
const ws = new WebSocket('wss://yourdomain.com/ws/notifications/');
ws.onopen = () => console.log('Connected');
ws.onmessage = (event) => {
const data = JSON.parse(event.data);
if (data.type === 'notification') {
showNotification(data.message);
}
};
ws.onclose = (event) => {
// Auto-reconnect sau 3 giây
setTimeout(() => reconnect(), 3000);
};
WebSocket không phải là replacement cho HTTP — nó bổ sung cho HTTP trong những use case cụ thể: chat, live updates, collaborative editing, gaming. Đừng dùng WebSocket cho những thứ HTTP đủ dùng.
Kết Luận
Django Channels mở rộng Django để hỗ trợ async protocols. Với Redis làm channel layer, bạn có thể scale WebSocket across multiple server instances. Bước tiếp theo: tìm hiểu Server-Sent Events (SSE) — đơn giản hơn WebSocket cho use case one-way (server → client).
Chưa có bình luận. Hãy là người đầu tiên!