websocket心跳节流的核心是按需可控执行而非减少次数,需用settimeout链式调用、检查连接状态、监听页面可见性、响应pong检测超时、消息极简化。

节流在 WebSocket 心跳包发送中,不是为了“减少心跳次数”,而是防止心跳逻辑失控导致资源浪费——比如定时器堆积、后台重复发包、页面隐藏时仍强行触发、或网络异常后盲目重试。真正省电省流量的关键,是让心跳只在必要时、以最小开销、按需执行。
心跳发送必须用 setTimeout 链式调用,而非 setInterval
setInterval 在页面后台、系统休眠、低端安卓机上极易失准,可能延迟几十秒才执行,或恢复时集中触发多次,造成无效发包和电量空耗。
- 每次成功发送 ping 后,再用 setTimeout 设置下一次,确保节奏可控、无堆积
- 发送前检查
ws.readyState === WebSocket.OPEN,避免连接已断还发包 - 若发送失败(如网络中断),立即清空当前 timeout,不递归下一轮
页面可见性决定心跳是否启用
用户切走标签页、锁屏、App 进入后台时,心跳不仅没必要,还会被浏览器节流甚至冻结,徒增 CPU 唤醒和电量消耗。
- 监听
document.addEventListener('visibilitychange', ...) - 页面隐藏时 clearTimeout 当前心跳定时器,暂停发送
- 页面重新可见时,立即补发一次 ping,并重置超时检测计时器
心跳响应要参与节流判断,不能只发不验
只发 ping 不等 pong,等于没做节流——它无法识别单向断连,反而会持续重发,浪费流量和电量。
- 每次发 ping 后启动一个独立的 timeout 检测(如 10 秒),未收到 pong 就判定异常
- 收到 pong 时,clearTimeout 对应的检测句柄,再启动新 timeout
- 避免多个 timeout 并发运行,否则一超时就批量触发重连,引发雪崩
心跳消息本身要极简且可丢弃
心跳不是业务数据,它的唯一目标是探活。体积大、带签名、加加密、走完整鉴权链路,都会增加 CPU 和带宽负担。
- 用纯 JSON 轻量结构:
{"type":"ping","ts":1721468400},不含业务字段 - 服务端收到后立即返回 pong,不做日志、不存库、不校验 token
- 客户端解析 pong 也只需检查
type === "pong",跳过时间戳校验等可选逻辑











