WebSocket保活必须依赖心跳机制,否则必然断连;需前后端协同实现Ping/Pong、Nginx调优、超时兜底及安全重连。

WebSocket连接断连,不是“偶尔出问题”,而是“不加心跳就必然断”。Nginx默认proxy_read_timeout 60秒、移动运营商NAT超时常为30–90秒、Kestrel或Uvicorn未设读取截止时间——这些都会在你毫无察觉时静默切断连接。心跳不是可选项,是保活刚需。
FastAPI后端:用SetReadDeadline + set_pong_handler防被动断连
浏览器无法发原生Ping帧,所以前端只能发{"type": "ping"}这类业务消息;后端必须能识别并响应,同时靠系统级Pong机制兜底。
- 必须在
websocket.accept()后立即调用websocket._socket.set_read_deadline()(需访问底层socket)或改用websockets库的set_ping_timeout() - 更稳妥的做法是用
websockets而非FastAPI原生WebSocket,它原生支持set_pong_handler:收到Ping自动回Pong,无需解析业务消息 - 服务端每30秒主动发一次Ping(用
send()发{"type": "ping"}),同时设置read_timeout=90——超时阈值应 ≥ 心跳间隔 × 2 - 别依赖
on_disconnect事件清理资源:TCP半开连接时该事件根本不会触发,得靠read_timeout异常捕获后手动close()
JavaScript前端:定时send() + 双重超时检测(客户端+服务端)
只发ping不检测pong,等于没做心跳。前端既要发,也要等回,还要自己盯住时间。
WebSocket 8.18.2 是该协议规范的一个重要迭代版本,主要优化了连接稳定性与数据传输效率。它通过全双工通信机制,允许客户端与服务器在单一长连接上实时交换数据,大幅降低传统 HTTP 轮询的开销。该版本增强了心跳保活、自动重连及二进制帧传输能力,适用于即时通讯、在线游戏及金融行情推送等低延迟场景,为开发者提供更可靠的实时网络交互基础。
- 连接打开后启动两个定时器:
setInterval(() => ws.send(JSON.stringify({type: "ping"})), 25000)(25秒发一次) - 收到任意消息(包括
{"type": "pong"})时,更新lastMessageTime = Date.now() - 另起一个
setInterval每15秒检查:if (Date.now() - lastMessageTime > 30000) { ws.close(4000, "heartbeat timeout"); } -
onclose和onerror中必须clearInterval所有定时器,否则内存泄漏+后台疯狂发包
Nginx代理配置:proxy_read_timeout必须大于心跳周期
哪怕前后端心跳写得再完美,Nginx在60秒掐一刀,一切归零。
- 关键三行配置缺一不可:
proxy_http_version 1.1、proxy_set_header Upgrade $http_upgrade、proxy_set_header Connection "upgrade" -
proxy_read_timeout建议设为86400(24小时),至少是心跳间隔的3倍;proxy_send_timeout同理 - 如果用Cloudflare,需关闭“Always Online”和“WebSockets”开关——它会缓存并中断长连接
- 检查日志里是否频繁出现
upstream timed out (110: Connection timed out),这是Nginx已动手的铁证
重连逻辑不能只靠setTimeout:指数退避+最大尝试次数必须硬编码
网络抖动时连续10次1秒重连,等于对服务端发起DDoS。
- 初始重连延迟设为
1000ms,每次失败翻倍,上限卡死在30000ms(30秒) - 记录重连次数,超过5次就弹提示“网络异常,请检查连接”,别让用户干等
- 重连前务必
ws?.close(),否则旧连接残留+新连接并发,服务器连接数虚高 - 重连成功后,要重置所有定时器、清空
lastMessageTime、重新订阅业务通道——别让旧状态污染新连接
最易被忽略的一点:心跳消息不能走业务路由。比如后端用if data.get("type") == "ping"分支处理,但某天你加了个新功能也用"ping"当指令,就会误判心跳。建议用独立字段如"__heartbeat__": true或直接发空字符串""(协议允许),避免语义冲突。

















