2026年1月26号晚上,我正在工位上收拾东西准备下班,监控群里突然弹出一条告警:实时看板的数据不刷新了。紧接着有同事甩来一张前端控制台的截图,刺眼的红色报错写着“stream disconnected before completion: websocket closed by server before res”。这个项目上线大半年,WebSocket连接一直很稳定,之前从来没出过这种问题。我当时第一反应是服务端挂了或者连接数被打满,可登录服务器一看,进程活着、端口正常监听、日志里也没有任何异常退出,这就有点意思了。
后来我花了好几个小时,从浏览器、客户端工具、Nginx代理、业务代码一路排查过去,最后发现“凶手”确实是WebSocket,但祸根藏在一层平时根本不会留意的代理超时配置里。今天把这次完整的排查过程写出来,希望能帮到正在被WebSocket连接问题折磨的朋友。如果你在实现实时推送、在线状态、消息通知这类功能时,也遇到连接偶尔断开、过一会儿又自己恢复的怪问题,这篇文章基本可以当一份排查手册来用。
1. 问题现场:现象与最初的怀疑
1.1 现象记录:报错信息与业务影响
先说清楚当时的现象。前端的实时看板页原本应该每隔十几秒就收到服务端推送的数据,可报错之后页面上的数字全部冻结了,要手动刷新一次才能恢复一小会儿,过几十秒又断掉。控制台里反复出现那句“stream disconnected before completion: websocket closed by server before res”,这句话拆开看意思很明确:客户端这边有一个流式请求还没有读到完整结果,连接就被对端关闭了。
需要说明的是,这个报错本身并不一定来自浏览器原生的WebSocket API,很多框架在底层封装了长连接或订阅功能(比如GraphQL的subscription、SSE、各种实时消息SDK),它们会在连接异常断开后抛出类似的提示。关键信息其实是后半句“websocket closed by server before res”——连接是被服务端关闭的,而且是在客户端等响应的时候。这直接帮我缩小了排查范围:不是客户端代码主动断的,也不是网络彻底断掉,而是服务端那边“主动”把连接掐了。
业务影响也远比一个看板要广,这个项目里所有依赖WebSocket的功能都受到了牵连:实时告警推送停了、在线用户状态全部显示异常、还有一个内部报表的自动刷新也失效了。虽然核心业务接口还是能通过HTTP正常访问,但只要是走长连接的数据,基本全军覆没。这种“部分功能挂掉、进程还在运行”的状态,最让人头疼。
1.2 我先排除了哪些“正常”问题
出现这种问题,大多数人第一反应是服务端挂了,我也不例外,但很快就把常规项都排除掉了:
- 服务端进程与端口:主进程还在运行,没崩,端口也一直有监听,日志里找不到panic或者fatal的痕迹。
- 数据库与缓存:数据库连接池正常,慢查询没有明显增加,Redis的读写延迟也正常。这说明不是后端资源问题拖垮了连接。
- 网络连通性:从我的电脑直接ping服务端,延迟正常,丢包率为零;用telnet测一下WebSocket对应的端口,也能正常连通。基础网络层面没有明显故障。
- 时间同步:客户端和服务器的系统时间差在1秒以内,排除了因为时间偏差导致TLS或认证失败的可能。
- 最近是否有发版:我特意去问了运维同学,最近一次发版是大概一周前,当天没有发布记录。所以基本可以排除“新代码引入回归bug”这个原因。
这些常规项排完之后,问题更蹊跷了。服务端看着一切正常,但客户端就是连接不稳定,而且不是所有人都受影响,有些人的页面已经恢复正常,有些人的还是一直在断。这种“玄学”现象通常意味着问题出在链路中间层或者某个超时配置上,而不是一个简单粗暴的宕机。
1.3 最初怀疑的几个方向
我在把所有常规项排除掉之后,脑子里大概列了四个怀疑方向,这里也分享给大家,因为后续排查基本都是围绕这四个方向展开的:
- 服务端连接数打满:如果服务器允许的最大WebSocket连接数已经到了上限,新的连接进不来,旧连接也可能被强制踢掉。但看了监控,当前连接数才占总配额的三成左右,这个方向基本排除。
- 客户端网络环境差异:有些同事在公司内网访问,有些在外网,防火墙或者代理策略不一样,长连接本来就被很多安全设备盯得很紧,容易误杀。
- 浏览器版本问题:有同事提到“谷歌浏览器高版本无法启用WebSocket”这个说法,说实话当时我愣了一下,后来仔细想了想,高版本浏览器确实有一些更严格的安全策略会影响WebSocket连接,并不是完全“无法启用”,这个需要重点排查。
- 中间代理层配置异常:我们服务端前面挂了一层Nginx做反向代理,WebSocket这种长连接对代理层的超时设置非常敏感,如果相关参数不对,就会出现连接自动断开的问题。当时这个方向被我排在了后面,毕竟这套配置上线半年都没动过,谁能想到它才是“幕后黑手”。
现在回头看,这个怀疑顺序基本合理,但唯一的失误就是太相信“配置没动过就不会出问题”这个错觉。很多线上事故恰恰是长期潜伏的配置在某个特定条件的触发下才爆发的。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 一步步排查:从浏览器到协议
2.1 先看握手机制:状态码101与响应头
排查WebSocket问题,第一步永远是确认握手是否成功。WebSocket本质上是基于HTTP的协议升级:客户端发一个带 Upgrade: websocket 的HTTP请求,服务器如果同意,就返回状态码 101 Switching Protocols,之后双方在同一个TCP连接上双向收发数据帧。如果握手这一步都失败了,后面所有行为都是白搭。
我用Chrome DevTool
