WebSocket服务端心跳设计核心是“客户端发PING、服务端响应PONG并主动淘汰失联连接”,需双向超时对齐、轻量状态管理、结构化可观测性。

WebSocket 服务端心跳保持不是“发不发”的问题,而是“怎么配合、怎么响应、怎么清理”的系统性设计。核心原则是:服务端不主动发 PING,但必须及时响应客户端 PING,并主动淘汰失联连接。
服务端只响应,不发起
浏览器端 WebSocket API 不暴露发送 PING 帧的能力,所以服务端不应依赖自己发 PING 来驱动心跳。生产环境统一由客户端定时发送 PING(或自定义 ping 消息),服务端只需监听并立即返回 PONG(或 pong)。这样既规避了前端限制,又把控制权交给更易管理的客户端。
- 禁用服务端自动 pingInterval(如 ws 库中设为 0)
- 接收任意格式的心跳消息(如
{"type":"ping","id":"c123"}或纯字符串"ping"),统一响应{"type":"pong"} - 响应必须同步、低延迟,避免在业务逻辑中排队处理
超时判定要双向对齐
服务端需独立维护连接活跃度,不能只依赖客户端是否发来 PING,而要结合时间窗口做主动判断。关键不是“等多久没收到”,而是“多久没收到就该关”。
WebSocket 8.18.2 是该协议规范的一个重要迭代版本,主要优化了连接稳定性与数据传输效率。它通过全双工通信机制,允许客户端与服务器在单一长连接上实时交换数据,大幅降低传统 HTTP 轮询的开销。该版本增强了心跳保活、自动重连及二进制帧传输能力,适用于即时通讯、在线游戏及金融行情推送等低延迟场景,为开发者提供更可靠的实时网络交互基础。
- 记录每个连接最后一次收到 PING 的时间戳
- 设置服务器侧超时阈值(建议为客户端心跳间隔的 2 倍,如客户端每 30s 发一次,服务端 60s 未收到即断连)
- 使用连接空闲检测(如 Node.js ws 库的
pingTimeout参数)替代轮询,降低 CPU 开销 - 断开前可发送
close帧并附带 reason(如 "idle timeout"),便于客户端区分异常类型
连接状态要轻量可回收
服务端心跳逻辑不能引入额外状态负担。每个连接应尽量无状态或仅保留必要元数据,避免内存泄漏和资源堆积。
- 不为心跳单独建长连接池或会话缓存
- 在
connection事件中绑定pong处理器(如ws.on('pong', () => { ws.lastPing = Date.now(); })),利用原生 PONG 事件省去 JSON 解析 - 关闭连接时调用
ws.terminate()而非ws.close(),强制释放底层 socket,防止 TIME_WAIT 积压 - 配合负载均衡器(如 Nginx)调整
proxy_read_timeout和proxy_send_timeout,确保与服务端超时策略一致
异常闭环要可观测
心跳失败不是静默事件,服务端需留下可追踪痕迹,支撑运维与问题定位。
- 对超时断连、PONG 失败、非法心跳格式等场景打结构化日志(含 clientIP、connectionId、lastPingTime)
- 暴露 /health/ws-stats 等接口,统计当前活跃连接数、平均心跳延迟、超时断连率
- 将高频断连 IP 或 User-Agent 上报告警系统,识别网络策略变更或恶意探测
- 避免在心跳路径中触发重试、降级或外部调用,保证该路径极致轻量

















