浏览器节能机制导致Websocket断连的巨坑!
你踩过吗?浏览器节能机制导致WebSocket断连的坑~~~
近期,在使用WebSocket(WS)连接时遇到了频繁断连的问题。这种情况在单个用户上每天发生数百次。尽管利用了 socket.io
的自动重连机制能够在断连后迅速恢复连接,但这并不保证每一次重连都能成功接收WS消息。经过一系列排查和测试,最终发现问题的根本原因竟然是——浏览器的节能机制。
浏览器节能机制简介
现代浏览器为了减少电能消耗和提高电池续航能力,逐渐引入了节能机制。这些机制包括但不限于:
- 降低空闲标签页的CPU使用率
- 减少后台JavaScript的执行频率
- 限制定时器的精确度
尽管这些措施显著提高了设备的能效,但也给前端开发带来了新的挑战,尤其是在涉及定时任务或WebSocket连接时。
WS频繁断连原因分析
查阅 socket.io
官网文档,发现服务端配置的 pingTimeout
和 pingInterval
两个参数与WS心跳机制密切相关。
WS连接中心跳机制的关键点:
- 服务器发送
ping
,如果客户端在pingTimeout
毫秒内没有用pong
应答,则服务器认为连接已关闭。 - 客户端如果在
pingInterval + pingTimeout
毫秒内未收到服务器的ping
,则客户端也会认为连接已关闭。
在高版本的 socket.io
中,心跳由服务端定时发起,而在 2.x 版本中,心跳由客户端定时发起。当浏览器在后台运行时,即使设置了每秒触发的定时器,实际触发间隔却被延长至每分钟一次,这远远超过了 pingInterval + pingTimeout
的设置时间,导致WS频繁断连。
WS频繁断连的解决方法
1. 升级 socket.io
到最新版本
升级到 socket.io
4.x 版本,由服务器定时发起心跳,从而避开浏览器节能机制对定时器的影响。
2. 自定义WS心跳事件
为了减少升级带来的影响,可以使用自定义心跳事件,在服务端定时发送 custom-ping
消息。
// 客户端代码
io.on('custom-ping', function () {
io.emit('custom-pong', Date.now());
});
// 服务端代码
io.on('connection', (socket) => {
console.log('New client connected');
// 定时发送自定义ping消息
const pingInterval = setInterval(() => {
socket.emit('custom-ping', Date.now());
}, 10000); // 每10秒发送一次
// 监听自定义pong消息
socket.on('custom-pong', (data) => {
console.log('Pong received:', data);
});
socket.on('disconnect', () => {
clearInterval(pingInterval);
console.log('Client disconnected');
});
});
3. 使用 setTimeout
函数的正确姿势
直接使用 setTimeout
仍然可能会丢失精度,正确的使用方式如下:
// 客户端代码
let timer;
const onHeart = () => {
if (timer) {
clearTimeout(timer);
}
timer = window.setTimeout(() => {
socket.emit('custom-ping', Date.now());
}, 5000);
};
socket.on('custom-pong', onHeart);
// 服务端代码
socket.on('custom-ping', () => {
socket.emit('custom-pong', Date.now());
});
4. 使用 Web-Workers
在 Web-Workers 线程内发起定时任务,不受浏览器节能机制的限制。
小结
WS频繁断连的原因总结:
- 使用了低版本(2.x)的
socket.io
- 在客户端每5秒定时发送心跳
- 浏览器后台运行时触发节能机制,限制定时器的精度,由每5秒变为实际的每分钟执行一次
- 每分钟执行一次远超
pingTimeout
设置时间,导致WS断开连接 socket.io
内置的重连机制立即重连成功,但实际日志显示每分钟重连一次
通过上述方法,我们可以有效解决浏览器节能机制对定时器精度降低带来的WebSocket频繁断连问题,从而提升用户体验。
随着浏览器技术的发展,节能机制无疑会越来越完善,但这也给前端开发带来了新的挑战。了解和适应这些变化,采用正确的策略来解决由此引发的问题,对于开发高质量的前端应用至关重要。