对的,你的理解没错:
如果内网 Spring Boot 作为 WebSocket 客户端 连外网的 WebSocket 服务端,那么理论上这两个进程之间会一直保持一个 TCP 长连接。
但是“理论上一直保持”≠“实际上永远不会断”。在真实环境里,WebSocket 链接可能因为很多原因断开,需要你在客户端做心跳检测 + 重连机制。
什么时候会断开?
常见原因分几类:
1.网络层原因
防火墙 / NAT 超时:
很多防火墙/NAT 对“空闲 TCP 连接”有限制,比如 60 秒/300 秒没数据就认为连接死了,直接丢弃。网络抖动 / 断网:
内网服务器访问外网的出口网络不稳定,包丢了,连接就会断。代理/负载均衡策略:
有些外网服务器放在云上,前面有 Nginx/ELB 等代理,它们会对长连接有限制(如 idle timeout 60s/120s)。
2.
应用层原因
服务端主动关闭:
外网 WebSocket 服务端重启、升级、超时策略(例如某些云服务默认 1 小时断一次)。客户端异常:
内网 Spring Boot 内存溢出、进程重启、线程池阻塞等。
3.
安全层原因
证书过期/错误:wss:// 握手失败。
token 过期:如果你用 Bearer token 做认证,token 失效时外网可能关闭连接。
如何应对?
所以几乎所有生产可用的 WebSocket 客户端都会做三件事:
心跳机制(keepalive)
客户端定时发 ping(或自定义心跳包),服务端 pong 回复。
典型间隔:30 秒 ~ 1 分钟。
作用:防止 NAT/防火墙踢掉空闲连接 + 检测连接是否还活着。
断线重连
一旦连接异常关闭,客户端进入重连流程。
重连不要死循环,要指数退避(比如 1s, 2s, 4s, 8s… 上限 1min)。
避免雪崩:每个实例加随机抖动(jitter)。
会话恢复
断开期间外网可能有新消息没推过来。
解决方案:
内网重连时带上“上次处理的 messageId”,外网从这个位置继续推送(offset)。
或者外网维护“未 ACK 消息队列”,客户端重连后重新补发。
实际效果
在理想稳定网络环境:连接可以跑几天/几周不掉。
在现实互联网环境:几小时 ~ 一天内可能就断过,需要心跳+重连。
在企业内网环境:防火墙/NAT 超时是最常见原因,不加心跳基本 5~10 分钟就掉线。
✅ 总结一句:
是的,WebSocket 设计就是要保持长连接;但在实际部署中,连接一定会偶尔断掉,必须做心跳检测和断线重连,最好还要有“消息重放”机制保证数据完整性。
要不要我帮你给出一个 Spring Boot WebSocket 客户端的心跳 + 自动重连代码样例?这样你直接能在内网系统里用,不用自己踩坑。