Chrome 里每隔 30 秒发一次 ping,服务端 ping 计数一直是 0,客户端 90 秒后触发 onclose,code 1006 而 reason 是空字符串。乍看像缓存或者权限的锅,追下去往往不是。
ws.send(JSON.stringify({ type: 'ping', t: Date.now() }));
setInterval(() => ws.send(JSON.stringify({ type: 'ping' })), 30000);
追下去是这么回事:公司出口的 HTTP 代理只转发数据帧,控制帧 ping 和 pong 在转发时被丢弃,两边都以为对面还活着,其实链路早被代理判成空闲。控制帧不带业务数据,中间设备做过滤时经常直接扔。
「心跳帧被拦截」相关的日志我一般连着看前后五分钟,单看一条报错经常判断不出是哪一步失败。一次只改一处,改完立刻验证,几个改动叠在一起,出问题就说不清是谁导致的。
先做最小复现,能稳定跑出来的问题,排查成本会低一个数量级。
具体做法是,把心跳改成普通文本帧走业务通道;服务端收到后回一条 pong 消息并在日志里打点;协议层 ping 保留着 兼容直连场景;观察日志里 pong 计数是否和 30 秒一次对得上。
验收就看一点,服务端每分钟能收到两条心跳 连接保持一小时没有断开。
