Nginx默认proxy_read_timeout=60秒会静默断开空闲WebSocket连接,需在location块中配置proxy_read_timeout 86400、proxy_send_timeout 86400、proxy_buffering off、proxy_cache off,并透传Upgrade和Connection头确保101握手成功。

WebSocket频繁断开,90%不是代码写错了,而是服务端和前端的超时参数没对齐,或者心跳机制根本没生效。
为什么Nginx会默默杀死你的WebSocket连接
Nginx默认proxy_read_timeout是60秒,只要60秒内没收到后端响应(包括pong),它就直接断开TCP连接,且不通知客户端。你看到的1006错误、控制台无日志、服务端也收不到onclose,大概率就是它干的。
- 必须在
location /ws/块里显式配置:proxy_read_timeout 86400、proxy_send_timeout 86400 - 务必加上
proxy_buffering off和proxy_cache off,否则消息可能被缓存延迟甚至丢弃 - 检查
proxy_set_header Upgrade $http_upgrade和proxy_set_header Connection "upgrade"是否完整——缺一行,握手就失败,返回200而不是101 - 如果用的是AWS ALB,注意它不支持调大空闲超时,只能把心跳间隔压到
服务端心跳参数怎么设才不冲突
心跳不是“开了就行”,服务端发ping、客户端回pong、双方超时阈值必须错开,否则会互相触发断连。
- Swoole中:
heartbeat_check_interval(检测频率)应小于heartbeat_idle_time(最大空闲时间),例如设为30和60 - ASP.NET Core:
KeepAliveInterval设为30秒,但服务端ReadTimeout要设为至少90秒,留出pong响应缓冲 - Node.js
ws库:用pingInterval和pingTimeout,比如pingInterval: 25000、pingTimeout: 5000,确保客户端有足够时间处理并回包 - Python
websockets:必须手动调用conn.ping()并监听pong,不能只依赖ping_interval,否则弱网下容易假死
前端重连逻辑最容易踩的三个坑
单纯监听onclose然后new WebSocket(),不出三天就会被用户投诉“疯狂闪退”。
立即学习“前端免费学习笔记(深入)”;
-
event.code === 1000代表正常关闭,别重连;event.code === 1006或event.code >= 4000才该触发重连流程 - 用
isReconnecting布尔变量锁住重连入口,避免onclose被连续触发多次,生成多个并发连接实例 - 重连前先
wsRef?.close(4999, "reconnect"),再清空引用,否则旧连接残留会占用fd、干扰新连接握手 - token过期类错误(如
reason含"token_expired")必须先调API刷新凭证,再重连,否则陷入“断→重连→401→再断”死循环
心跳消息格式不对,比不发还危险
很多前端直接socket.send("ping"),结果服务端解析失败,既不回pong也不报错,连接在60秒后被Nginx静默掐断。
- 必须和服务端约定好心跳载荷格式,推荐JSON:
socket.send(JSON.stringify({ type: "ping" })) - 服务端收到后必须原样回
{"type":"pong"},前端用onmessage监听并重置本地心跳计时器 - 别用
WebSocket.PING常量(浏览器不支持),也别依赖binaryType发二进制——文本模式最稳 - 加一个5秒级的“未收到pong”兜底:启动心跳定时器后,同时起一个
setTimeout,超时就ws.close(4999)主动断开,强制走重连逻辑
真正难的不是实现心跳或重连,而是让服务端超时、代理层超时、客户端心跳周期、重连退避节奏全部咬合在一个安全窗口里——差5秒,就可能在凌晨三点触发全量重连风暴。


















