在Django-Ninja框架中实现消息推送全过程
随着Web应用的发展,实时通信变得越来越重要。消息推送技术允许服务器主动向客户端发送数据,而不是客户端不断地轮询服务器。在Django-Ninja框架中实现消息推送不仅可以提升用户体验,还可以增强应用的功能性。本文将详细介绍如何在Django-Ninja项目中实现消息推送,并提供一个完整的示例。
一、消息推送技术的选择
在实现消息推送之前,我们需要选择合适的技术栈。常用的几种技术包括WebSocket、Server-Sent Events (SSE) 和长轮询(Long Polling)等。考虑到WebSocket提供了双向通信的能力,并且已经成为Web实时通信的标准,本文将采用WebSocket作为消息推送的技术方案。
二、准备工作
## 步骤 1: 环境搭建
确保你有一个基本的Django-Ninja项目。如果没有,可以通过以下命令创建一个新的项目:
# 创建Django项目
django-admin startproject myproject
cd myproject
# 安装Django-Ninja
pip install django-ninja
接下来,我们将使用Channels库来实现WebSocket的支持。Channels是Django的一个扩展库,允许Django处理异步请求和WebSockets。
pip install channels daphne
修改myproject/settings.py
,启用Channels:
INSTALLED_APPS = [
...
'channels',
]
ASGI_APPLICATION = 'myproject.routing.application'
CHANNEL_LAYERS = {
'default': {
'BACKEND': 'channels.layers.InMemoryChannelLayer',
},
}
不太明白可以查看另一篇文章:https://blog.csdn.net/tombosky/article/details/141861940
三、配置路由
## 步骤 2: 配置ASGI路由
我们需要在myproject/routing.py
中配置WebSocket路由:
from channels.auth import AuthMiddlewareStack
from channels.routing import ProtocolTypeRouter, URLRouter
from django.core.asgi import get_asgi_application
import myapp.routing # 导入应用级别的路由配置
application = ProtocolTypeRouter({
"http": get_asgi_application(),
"websocket": AuthMiddlewareStack(
URLRouter(
myapp.routing.websocket_urlpatterns
)
),
})
同时,在myapp/routing.py
中定义具体的路由:
from django.urls import re_path
from . import consumers
websocket_urlpatterns = [
re_path(r'ws/socket-server/', consumers.MyConsumer.as_asgi()),
]
四、创建消费者
## 步骤 3: 创建WebSocket消费者
在myapp/consumers.py
中定义一个WebSocket消费者类:
from channels.generic.websocket import AsyncWebsocketConsumer
import json
class MyConsumer(AsyncWebsocketConsumer):
async def connect(self):
self.room_group_name = 'test'
# 加入room group
await self.channel_layer.group_add(
self.room_group_name,
self.channel_name
)
await self.accept()
async def disconnect(self, close_code):
# 离开room group
await self.channel_layer.group_discard(
self.room_group_name,
self.channel_name
)
# 接收来自WebSocket的消息
async def receive(self, text_data):
text_data_json = json.loads(text_data)
message = text_data_json['message']
# 向room group发送消息
await self.channel_layer.group_send(
self.room_group_name,
{
'type': 'chat_message',
'message': message
}
)
# 接收来自room group的消息
async def chat_message(self, event):
message = event['message']
# 发送消息给WebSocket
await self.send(text_data=json.dumps({
'message': message
}))
五、发送消息
## 步骤 4: 发送消息
我们可以在视图中调用group_send
方法来发送消息给特定的房间组。首先,确保你已经在myproject/settings.py
中配置了任务队列,比如RabbitMQ或Redis。
# 在settings.py中添加
CELERY_BROKER_URL = 'redis://localhost:6379/0'
CELERY_RESULT_BACKEND = 'redis://localhost:6379/0'
接下来,安装Celery和Redis:
pip install celery redis
然后,在myapp/tasks.py
中定义一个发送消息的任务:
from celery import shared_task
from asgiref.sync import async_to_sync
from channels.layers import get_channel_layer
@shared_task
def send_message(message):
channel_layer = get_channel_layer()
async_to_sync(channel_layer.group_send)(
'test',
{
'type': 'chat_message',
'message': message,
}
)
六、集成API端点
## 步骤 5: 创建API端点
在myapp/api.py
中创建一个API端点来触发消息发送:
from ninja import Router
from ninja import Schema
from .tasks import send_message
router = Router()
class MessageIn(Schema):
message: str
@router.post("/send-message/")
def send_websocket_message(request, payload: MessageIn):
send_message.delay(payload.message)
return {"success": True}
七、运行项目
## 步骤 6: 启动Django项目和Celery worker
首先启动Django项目:
python manage.py runserver
然后启动Celery worker:
celery -A myproject worker --loglevel=info
最后,启动ASGI服务器(使用daphne):
daphne myproject.asgi:application
八、前端实现
## 步骤 7: 前端连接WebSocket
在前端JavaScript代码中建立WebSocket连接,并监听消息:
var socket = new WebSocket(`ws://${window.location.host}/ws/socket-server/`);
socket.onopen = function(e) {
console.log("Connection established!");
};
socket.onmessage = function(event) {
const data = JSON.parse(event.data);
console.log(data.message);
};
socket.onclose = function(event) {
console.log('Socket is closed.', event);
};
九、测试
## 步骤 8: 测试消息推送
通过API端点发送消息:
curl -X POST http://localhost:8000/send-message/ -H "Content-Type: application/json" -d '{"message":"Hello from Django!"}'
前端控制台应该接收到消息,并打印出来。
十、总结
本文详细介绍了如何在Django-Ninja框架中实现基于WebSocket的消息推送功能。通过配置Channels、定义WebSocket消费者、创建Celery任务以及集成API端点,我们成功实现了消息的实时传输。此外,还展示了如何从前端JavaScript代码中连接WebSocket,并监听服务器发送的消息。
完整代码示例
下面是完整的代码示例,包括settings.py
、routing.py
、consumers.py
、tasks.py
、api.py
以及前端JavaScript代码。
# myproject/settings.py (部分配置)
INSTALLED_APPS = [
...
'channels',
]
ASGI_APPLICATION = 'myproject.routing.application'
CHANNEL_LAYERS = {
'default': {
'BACKEND': 'channels.layers.InMemoryChannelLayer',
},
}
CELERY_BROKER_URL = 'redis://localhost:6379/0'
CELERY_RESULT_BACKEND = 'redis://localhost:6379/0'
# myproject/routing.py
from channels.auth import AuthMiddlewareStack
from channels.routing import ProtocolTypeRouter, URLRouter
from django.core.asgi import get_asgi_application
import myapp.routing
application = ProtocolTypeRouter({
"http": get_asgi_application(),
"websocket": AuthMiddlewareStack(
URLRouter(
myapp.routing.websocket_urlpatterns
)
),
})
# myapp/routing.py
from django.urls import re_path
from . import consumers
websocket_urlpatterns = [
re_path(r'ws/socket-server/', consumers.MyConsumer.as_asgi()),
]
# myapp/consumers.py
from channels.generic.websocket import AsyncWebsocketConsumer
import json
class MyConsumer(AsyncWebsocketConsumer):
async def connect(self):
self.room_group_name = 'test'
await self.channel_layer.group_add(
self.room_group_name,
self.channel_name
)
await self.accept()
async def disconnect(self, close_code):
await self.channel_layer.group_discard(
self.room_group_name,
self.channel_name
)
async def receive(self, text_data):
text_data_json = json.loads(text_data)
message = text_data_json['message']
await self.channel_layer.group_send(
self.room_group_name,
{
'type': 'chat_message',
'message': message
}
)
async def chat_message(self, event):
message = event['message']
await self.send(text_data=json.dumps({
'message': message
}))
# myapp/tasks.py
from celery import shared_task
from asgiref.sync import async_to_sync
from channels.layers import get_channel_layer
@shared_task
def send_message(message):
channel_layer = get_channel_layer()
async_to_sync(channel_layer.group_send)(
'test',
{
'type': 'chat_message',
'message': message,
}
)
# myapp/api.py
from ninja import Router
from ninja import Schema
from .tasks import send_message
router = Router()
class MessageIn(Schema):
message: str
@router.post("/send-message/")
def send_websocket_message(request, payload: MessageIn):
send_message.delay(payload.message)
return {"success": True}
前端JavaScript代码:
var socket = new WebSocket(`ws://${window.location.host}/ws/socket-server/`);
socket.onopen = function(e) {
console.log("Connection established!");
};
socket.onmessage = function(event) {
const data = JSON.parse(event.data);
console.log(data.message);
};
socket.onclose = function(event) {
console.log('Socket is closed.', event);
};