ASGI服务器不能用runserver直接跑Channels,因为Django默认runserver是WSGI服务器,仅支持HTTP,不支持WebSocket等长连接;Channels必须通过Daphne、Uvicorn等ASGI服务器启动,并配置正确ASGI_APPLICATION和routing。

为什么 ASGI 服务器不能用 runserver 直接跑 Channels
因为 Django 默认的 runserver 是 WSGI 服务器,只处理 HTTP 请求,不支持 WebSocket 或长连接。Channels 必须走 ASGI 协议,否则 consumers.py 根本不会被调用,前端连不上 WebSocket 时只会卡在 pending 状态。
实操建议:
- 必须用
daphne、uvicorn或hypercorn启动,例如:uvicorn myproject.asgi:application --reload -
asgi.py文件不能漏掉,且要确保application = ProtocolTypeRouter({ ... })包含"websocket"路由 - 开发时别改
settings.py里的WSGI_APPLICATION,它和 Channels 无关;关键看ASGI_APPLICATION是否指向正确路径
WebsocketConsumer 和 AsyncWebsocketConsumer 怎么选
同步 Consumer 会阻塞整个事件循环,只要一个客户端调用耗时操作(比如查数据库、发 HTTP 请求),其他所有连接都会卡住。除非你确定所有逻辑都是纯内存计算,否则默认用异步版本。
实操建议:
- 优先写
AsyncWebsocketConsumer,所有方法名带async,await调用异步操作 - 如果非要混用同步代码(比如老项目里没异步封装的 SDK),得用
async_to_sync()包一层,但这是补救手段,不是设计选择 -
connect()方法里别直接await database_sync_to_async(...)查用户——认证信息应该从self.scope["user"]拿,前提是配置了AuthMiddlewareStack
怎么让多个客户端收到同一消息(比如群聊广播)
Channels 自带的 channel_layer 是跨进程通信的基础,但很多人卡在“只发给自己”或“发出去没人收”。根本原因是组名(group name)没对齐,或者没在连接时加入组。
实操建议:
- 连接成功后立刻
await self.channel_layer.group_add("chat_room_123", self.channel_name) - 发消息时用
await self.channel_layer.group_send("chat_room_123", {"type": "chat.message", "text": "hi"}) -
type字段必须对应 Consumer 里定义的方法名(去掉点号,转成下划线),比如"chat.message"对应chat_message(self, event) - 别在
disconnect里忘了group_discard,否则组成员数会越积越多
前端连不上 ws://localhost:8000/ws/chat/ 的常见原因
浏览器控制台报 WebSocket connection to 'ws://...' failed,90% 不是代码问题,而是协议或路径错位。
实操建议:
- 确认 URL 是
ws://(开发)或wss://(生产),不是http://;Django 开发服务器不代理 WebSocket,不能通过http://localhost:8000/ws/...访问 - URL 路径必须和
routing.py里URLRouter配置完全一致,包括结尾斜杠——re_path(r"^ws/chat/$", consumers.ChatConsumer.as_asgi())就只能用/ws/chat/,少个/就 404 - 如果用了 Nginx 反向代理,必须显式透传 WebSocket 头:
proxy_set_header Upgrade $http_upgrade;和proxy_set_header Connection "upgrade";
最常被忽略的一点:Consumer 类里没写 as_asgi(),或者 routing.py 引入的是类而不是 as_asgi() 实例——这会导致 ASGI 应用启动失败,但错误日志可能藏在 daphne/uvicorn 进程里,前端只看到连接拒绝。










