WebSocket 心跳机制
服务端主动发送 WebSocket 协议层 Ping 帧以保持连接存活。
概述
为防止中间代理、负载均衡器或防火墙在长时间无数据交互后断开连接,服务端在连接建立后立即启动心跳任务。
实现方式
采用 WebSocket 协议层 Ping 帧(jakarta.websocket.RemoteEndpoint.Basic#sendPing),客户端 WebSocket 实现自动回复 Pong,无需应用层代码配合。
| 对比 | 协议层 Ping | 应用层心跳消息 |
|---|---|---|
| 客户端改动 | 无需 | 需识别并回复 |
| 占用消息通道 | 否 | 是 |
触发 @OnMessage | 否 | 是 |
参数
| 参数 | 值 | 说明 |
|---|---|---|
| Ping 间隔 | 25 秒 | HEARTBEAT_INTERVAL_SECONDS |
| 空闲超时 | 60 秒 | MAX_IDLE_TIMEOUT_MS,略大于 2 倍 Ping 间隔 |
设计依据
- 大多数代理/负载均衡器空闲超时为 60–120s,25s 间隔确保在超时前至少有 2 次心跳机会
- 60s 空闲超时:若连续 2 次 Ping 无 Pong,判定连接已断
时间轴:
0s ─── 25s(Ping#1) ─── 50s(Ping#2) ─── 60s(idle timeout)
↓ 丢包 ↓ 响应 → 连接保持
生命周期
OnOpen 认证成功
├─ session.setMaxIdleTimeout(60000)
├─ startHeartbeat(session)
│ └─ scheduleAtFixedRate(每25s sendPing)
│
├─ ... 连接存活 ...
│
├─ OnClose / Ping 失败
│ └─ stopHeartbeat(session)
│ └─ cancel ScheduledFuture
│
└─ Spring 容器关闭
└─ @PreDestroy destroy()
└─ shutdown heartbeatExecutor
客户端无需额外处理
标准 WebSocket 客户端自动响应 Pong。连接断开时正常触发 onclose 事件,客户端重新连接即可。
const ws = new WebSocket("ws://ydzx.panretro.com/external/api/v1/im/imWebSocket/" + token);
ws.onclose = function(event) {
console.log("连接断开, code:", event.code);
// 可在此处实现重连逻辑
};
涉及文件
src/main/java/com/fareast/ntg/external/ws/ImWebSocketConnect.java
关键字段和方法:
heartbeatExecutor— 单线程调度器heartbeatFutures— Session → ScheduledFuture 映射startHeartbeat(Session)— 启动心跳stopHeartbeat(Session)— 停止心跳destroy()— 容器关闭时清理