Django原生不支持WebSocket,因其同步架构与WebSocket长连接、异步、双向特性冲突;必须通过Channels+ASGI实现,且runserver仅在Channels≥4.0时提供有限开发兼容,不支持channel_layer、多进程或真实负载。

Django 原生不支持 WebSocket,强行用 runserver 访问 ws:// 会直接 404 或 502;必须用 Channels + ASGI 才能跑通,且开发期也得注意 runserver 的兼容边界。
为什么 runserver 有时能连上 WebSocket,有时又报错?
新版 Channels(≥4.0)在开发模式下对 runserver 做了有限兼容:它会把 WebSocket 升级请求转给内置的异步处理器。但这只适用于单进程、无 Redis、无跨 worker 广播的简单场景。
- 一旦你用了
channel_layer(比如channels_redis),runserver就无法正确分发消息——它没启动真正的 channel layer worker - 前端连
ws://localhost:8000/ws/chat/成功,但发消息没响应,大概率是 consumer 没收到,而非连接失败 - 生产环境必须用
daphne或uvicorn --lifespan on启动,runserver不是替代方案
AsyncWebsocketConsumer 和 SyncWebsocketConsumer 怎么选?
别默认选 AsyncWebsocketConsumer。同步版在多数广播类场景(如聊天室通知)更稳,且调试友好;异步版适合高并发 I/O 密集操作(如转发消息前查 DB+调外部 API)。
-
AsyncWebsocketConsumer要求所有方法(connect、receive、disconnect)都用async def,且不能混用self.send()和await self.send() -
SyncWebsocketConsumer允许你在receive里直接调 ORM,不用database_sync_to_async包一层,但阻塞时会卡住整个 consumer 实例 - 如果你只是做「进群 → 加组 → 广播消息」,用同步版更少出错;真要处理上千连接且每条消息都要查缓存+发 HTTP 请求,再切异步
WebSocket 路由配错的典型表现和修复点
路径不匹配、协议类型漏写、中间件顺序错,都会导致连接建立后立刻断开,浏览器控制台显示 WebSocket connection to 'ws://...' failed。
-
routing.py中必须用ProtocolTypeRouter,且'websocket'键不能拼错(不是'ws'或'websockets') -
URLRouter的路径要和前端new WebSocket('ws://.../ws/chat/')的末尾一致,多一个/或少一个都 404 -
AuthMiddlewareStack必须包在URLRouter外层,否则self.scope['user']是AnonymousUser,哪怕用户已登录 - 检查
ASGI_APPLICATION = 'myproject.asgi.application'是否指向正确的asgi.py,里面要有ProtocolTypeRouter,而不是旧版 WSGI 风格的get_asgi_application()
channel_layer 配置不当导致消息不广播
Consumer 收到消息却无法推给其他客户端,90% 是 channel_layer 没生效或配置错层类型。
- 开发时用内存层够用:
CHANNEL_LAYERS = {'default': {'BACKEND': 'channels.layers.InMemoryChannelLayer'}},但重启服务后组信息全丢 - 只要涉及多进程(比如
daphne启多个 worker)、或需要持久化组状态,就必须用channels_redis,且redis地址不能写成localhost(Docker 环境常见坑) - 调用
async_to_sync(self.channel_layer.group_send)()时,如果 group 名含特殊字符(如空格、中文),Redis 会静默失败,建议只用字母+数字+下划线 - 用
self.channel_layer.group_add加入组后,务必在disconnect里显式group_discard,否则组成员数会越积越多
最常被忽略的是:consumer 实例生命周期和 channel layer 组状态是分离的。连接断开不等于自动从组里踢出,这个清理动作必须手动写,否则“幽灵用户”会持续接收消息。


















