用 websockets 实现低延迟 web 即时通讯需重点处理连接管理、消息广播过滤与异常清理:必须显式启动事件循环,用 weakset 或 open 状态检查维护客户端集合,细粒度捕获 connectionclosed 异常,前端 url 补全 ws:// 协议头,服务端绑定 0.0.0.0 并透传 upgrade 头。

直接用 websockets 库就能跑通低延迟的 Web 即时通讯,但真正卡住人的从来不是“能不能连上”,而是连接管理、消息广播范围、异常断连后的状态清理——这些地方一漏,群聊就丢消息、用户反复重连、服务器内存悄悄涨满。
用 websockets 启动服务端必须加 asyncio.run() 或显式事件循环
Python 3.7+ 推荐用 asyncio.run(),老版本(如 3.6)得手动调 asyncio.get_event_loop()。别抄网上那些没指定 loop 的旧写法,否则在某些环境(比如 Jupyter 或某些 IDE)里会报 RuntimeError: no running event loop。
常见错误现象:
-
RuntimeError: no running event loop—— 没启动事件循环 -
RuntimeWarning: coroutine 'echo' was never awaited—— 把协程当同步函数调了
实操建议:
- 服务端入口统一用
asyncio.run(main())包一层,main()里调websockets.serve() - 别在非 async 函数里 await
websocket.send()或websocket.recv() - 如果集成进 Flask/Django,必须切到 ASGI 模式,
websockets不能和 WSGI 共存
connected 集合必须用 weakref.WeakSet 或带异常捕获的 remove
原始示例里用 set() 存客户端,看似简单,但 WebSocket 连接意外中断(比如用户关浏览器、网络抖动)时,websocket 对象不会自动从集合里消失,导致内存泄漏 + 广播时向已失效连接发数据,抛出 ConnectionClosed 异常并中断整个 gather。
实操建议:
- 用
weakref.WeakSet()替代普通set(),对象销毁后自动剔除 - 如果不换结构,至少在
broadcast前加if websocket.open:判断 -
try/except websockets.exceptions.ConnectionClosed必须包住每个send(),别只包外层 - 注册/注销逻辑要成对:connect 时 add,disconnect 时 remove,且 remove 必须在
finally块里
广播消息时别用 asyncio.gather(..., return_exceptions=True) 就以为万事大吉
return_exceptions=True 确实能让 broadcast 不因单个失败而中断,但它掩盖了真实问题:比如某个客户端已断开,但你还持续往它发数据,每次都会触发一次异常、一次重试开销、一次日志刷屏。这不是容错,是放任连接腐烂。
实操建议:
- 广播前先过滤:
[ws for ws in clients if ws.open] - 对每个
send()做细粒度异常捕获,记录断连 client ID,方便排查 - 别把 JSON 序列化塞进
gather里——json.dumps(data)是同步阻塞操作,放多了会拖慢 event loop;提前序列化好再传入 - 高并发下,
gather发太多并发 send 可能压垮对方 TCP 缓冲区,可考虑加限流或分批发送
客户端连接 URL 必须用 ws:// 或 wss://,别漏协议头
浏览器里用 new WebSocket("localhost:8765") 会失败,报 Invalid URL。这是最常被忽略的拼写细节,不是跨域问题,是 URL 格式不合法。
实操建议:
- 前端 JS 必须写全:
new WebSocket("ws://localhost:8765")或new WebSocket("wss://yourdomain.com") - 服务端绑定地址别只写
"localhost",开发时用"0.0.0.0",否则其他设备连不上 - 如果部署在 Nginx 后,必须透传
Upgrade和Connectionheader,否则握手失败,报400 Bad Request - 测试时优先用 Python 客户端脚本验证,排除浏览器 CORS 或缓存干扰
真正决定延迟的,从来不是协议本身,而是你有没有在连接断开的瞬间清理干净、有没有在广播前筛掉无效句柄、有没有让 JSON 序列化这种 CPU 操作污染 event loop。这些点不抠清楚,websockets 再快也白搭。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!











