第四十一章:WebSocket与BeatServer与JWT
1.需求
采用 django-channels 来实现 websocket 的消息实时通讯
2.介绍Django Channels
官方文档链接:Django-Channels
Channels 包装了 Django 的原生异步视图支持,允许 Django 项目不仅可以处理 HTTP,还可以处理需要长时间连接的协议 - WebSockets、MQTT、聊天机器人、业余无线电等。
它在实现这一点的同时保留了 Django 的同步和易用性,让您可以选择编写代码的方式 - 以 Django 视图的样式同步、完全异步或两者混合。除此之外,它还提供与 Django 的身份验证系统、会话系统等的集成,使您可以比以往更轻松地将仅 HTTP 项目扩展到其他协议。
Channels 还将这个事件驱动架构与通道层捆绑在一起,该系统允许您轻松地在进程之间进行通信,并将您的项目分成不同的进程。
如果您尚未安装 Channels,您可能需要先阅读安装 以进行安装。本介绍不是直接教程,但如果您愿意,您应该能够使用它来跟进并对现有的 Django 项目进行更改。
3.安装与注册
3.1 安装与配置
完成安装后,将 daphne、channels 添加到 INSTALLED_APPS 设置的开头:
pip install channels daphne
3.2 注册与启用频道层
settings.py 配置
配置 APP、入口路径、redis、通道层
# 注册 APP
INSTALLED_APPS = (
'daphne',
'channels',
"django.contrib.auth",
"django.contrib.contenttypes",
"django.contrib.sessions",
"django.contrib.sites",
...
)
# 新增配置:指定 ASGI 应用程序的入口
ASGI_APPLICATION = 'apps.ws.routing.application'
# redis 配置
REDIS_SETTING = {
"redis_host": os.environ.get("REDIS_HOST", "123.57.3.49"),
"redis_port": os.environ.get("REDIS_PORT", 6379),
"redis_password": os.environ.get("REDIS_PASSWORD", 'hukai.2026'),
}
# 启用频道层
CHANNEL_LAYERS = {
'default': {
'BACKEND': 'channels_redis.core.RedisChannelLayer',
'CONFIG': {
"hosts": [
f"redis://:{REDIS_SETTING['redis_password']}@{REDIS_SETTING['redis_host']}:{REDIS_SETTING['redis_port']}/0"],
"capacity": 1000, # default 100
"expiry": 60, # default 60
},
},
}
asgi.py 配置
注意:在 asgi.py 文件中修改 ASGI 应用实例入口,使用 get_default_application 替换原来的 get_asgi_application,这时 http 接口会从 ASGI_APPLICATION = 'apps.ws.routing.application' 入口函数进入
import os
import django
# from django.core.asgi import get_asgi_application
from channels.routing import get_default_application
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'core.settings')
django.setup()
application = get_default_application()
# application = get_asgi_application()
3.3 创建 ws app
# 注意在 INSTALLED_APPS 注册 APP
python manage.py startapp ws
4.实例
# 相关包
PyJWT: 安装 djangorestframework_simplejwt 时会自动安装该依赖
channels-redis
django_redis
项目地址:https://gitee.com/ysging/ws-django-channels.git
案例介绍:采用 django-channels 来实现 websocket 的消息实时通讯,BeatServer 实现通道层(Channel)定时任务发送,djangorestframework-simplejwt 生成 token,用于 websocket 的连接认证。
实现逻辑:通过 Django Channels 的 ASGI 路由配置,用于处理多种协议类型的请求(WebSocket、Channel 消息、HTTP),通过 ProtocolTypeRouter 根据协议类型分发请求到不同的处理模块。
路由设计:
application = ProtocolTypeRouter(
{
"websocket": JWTAuthMiddlewareStack(
URLRouter([
re_path(r"^ws/$", AsyncWSConsumer.as_asgi()),
])
),
# 通道层,可以不使用
"channel": ChannelNameRouter({
"periodic_sending": PeriodicConsumer.as_asgi(),
}),
'http': get_asgi_application()
}
)
功能拆解:
## WebSocket 配置解析
"websocket": JWTAuthMiddlewareStack(
URLRouter([
re_path(r"^ws/$", AsyncWSConsumer.as_asgi()),
])
)
# JWTAuthMiddlewareStack:自定义中间件栈,用于处理 JWT 认证(通常封装认证逻辑)
# URLRouter:根据 URL 路径路由 WebSocket 连接到对应的 Consumer
# re_path(r"^ws/$", ...):匹配 WebSocket 连接路径(如 ws://127.0.0.1:8002/ws/)
# AsyncWSConsumer:处理 WebSocket 消息的异步消费者类(需实现 connect(), receive() 等方法)
## Channel 消息配置解析
"channel": ChannelNameRouter({
"periodic_sending": PeriodicConsumer.as_asgi(),
}),
# ChannelNameRouter:根据通道名称(Channel Name)路由后台消息
# "periodic_sending":通道名称标识符(用于区分不同类型的后台任务)
# PeriodicConsumer:处理特定通道消息的消费者(通常用于定时任务或异步事件)
## HTTP 配置解析:就是原 asgi.py 中的 application 换了入口位置
'http': get_asgi_application()
# get_asgi_application():返回 Django 默认的 ASGI 应用,用于处理传统 HTTP 请求(兼容 Django 视图)
4.1 执行循序
# 以继承 AsyncJsonWebsocketConsumer 为例
from channels.generic.websocket import AsyncJsonWebsocketConsumer
# 开启链接时:
self.websocket_connect
self.connect
self.accept
# 接受消息时:
self.receive
self.receive_json
# 发送消息时:
self.send_json
self.send
# 关闭链接时:
self.websocket_disconnect
self.disconnect
class AsyncWSConsumer(AsyncJsonWebsocketConsumer):
async def websocket_connect(self, message):
pass
async def connect(self, message):
pass
async def accept(self, message):
# 接受传入套接字
pass
async def websocket_disconnect(self, code):
# 遍历组,从通道层当中移除
# 不必在手动实现
pass
async def disconnect(self, code):
pass
async def receive(self, code):
pass
async def receive_json(self, code):
pass
async def send_json(self, code):
pass
async def send(self, code):
pass
4.2 必要重写方法
# connect 或 websocket_connect:开启链接时实现用户认证
# receive 或 receive_json:接收请求参数并进行数据处理,通过 send_json 发送数据
4.3 启动
# 启动 daphne 服务
daphne core.asgi:application -b 0.0.0.0 -p 8001
4.4 访问
请求 ws 接口
可以使用线上在线工具:https://wstool.js.org/
网址:ws://127.0.0.1:8001/ws/?token=xxx
请求:{"option": "subscribe", "group": "get_usrs_list"}
获取 token
curl \
-X POST \
-H "Content-Type: application/json" \
-d '{"username": "admin", "password": "123456"}' \
http://localhost:8000/api/token/
4.5 配置入口
# 1.注意在 setting.py 文件中新增启动入口配置:ASGI_APPLICATION = 'apps.ws.routing.application'
在 ws app 下新建 routine.py,作为入口, 与配置保持一致。
5.BeatServer
Beatserver,django 频道的周期性任务调度器,与 channels 可实现 websocket 的定时发送功能。
官网:https://github.com/rajasimon/beatserver
5.1 安装
pip install beatserver
5.2 配置
beatserver在 settings.pyINSTALLED_APPS中添加
INSTALLED_APPS = [
'beatserver',
'channels',
'...'
]
5.3 beatconfig.py
定时任务配置文件,放在 core 目录下
from datetime import timedelta
from apps.ws.groups import beat_schedule
BEAT_SCHEDULE = {
'periodic_sending': beat_schedule
}
# 可以与 websocker 的调度内容放在一起
beat_schedule = [
{
'type': 'broadcast',
'message': {'group': 'online_state'},
'schedule': timedelta(seconds=60)
},
{
'type': 'broadcast',
'message': {'group': 'online_state2'},
'schedule': timedelta(seconds=60)
},
]
5.4 routing.py
路由
# 可以与 websocker 的调度内容放在一起
application = ProtocolTypeRouter({
"channel": ChannelNameRouter({
"testing-print": PrintConsumer,
}),
})
5.5 consumers.py
消费者
from channels.consumer import SyncConsumer
class PrintConsumer(SyncConsumer):
def test_print(self, message):
print(message)
5.6 启动
python manage.py beatserver
5.7 docker
docker-compose.yml 启动
version: "3"
services:
beatserver:
image: your-project/beatserver:latest
container_name: my_beatserver
command: /bin/sh -c "sleep 3 && python manage.py beatserver"
restart: always
environment:
- DEBUG=0
- DATABASE_HOST=postgres
- DATABASE_PORT=5432
- DATABASE_NAME=project
- DATABASE_USER=postgres
- DATABASE_PASSWORD=pswd123456
- REDIS_HOST=redis
- REDIS_PORT=6379
- REDIS_PASSWORD=123456
volumes:
- ./project/src:/deploy/backend
depends_on:
- redis
6.JWT
6.1 介绍
官方文档链接:https://django-rest-framework-simplejwt.readthedocs.io/en/latest/index.html
6.2 安装
pip install djangorestframework-simplejwt
6.3 配置
settings.py
INSTALLED_APPS = [
'rest_framework_simplejwt',
]
REST_FRAMEWORK = {
'DEFAULT_AUTHENTICATION_CLASSES': (
'rest_framework_simplejwt.authentication.JWTAuthentication',
)
}
# 一些 Simple JWT 的行为可以通过设置变量来定制
# 没要求可不配置
from datetime import timedelta
SIMPLE_JWT = {
"ACCESS_TOKEN_LIFETIME": timedelta(minutes=5),
"REFRESH_TOKEN_LIFETIME": timedelta(days=1),
...
}
urls.py
另外,在您的根urls.py文件(或任何其他 URL 配置)中,包含简单 JWTTokenObtainPairView和TokenRefreshView视图的路由:
from rest_framework_simplejwt.views import (
TokenObtainPairView,
TokenRefreshView,
)
urlpatterns = [
...
path('api/token/', TokenObtainPairView.as_view(), name='token_obtain_pair'),
path('api/token/refresh/', TokenRefreshView.as_view(), name='token_refresh'),
...
]
6.4 创建用户
python manage.py createsuperuser
6.5 用法
# 请求
curl \
-X POST \
-H "Content-Type: application/json" \
-d '{"username": "admin", "password": "123456"}' \
http://localhost:8000/api/token/
# 返回值
refresh:长期有效的刷新令牌
access:短期有效的访问令牌
{
"refresh": "xxxxx",
"access": "xxxxx"
}

浙公网安备 33010602011771号