Node.js + ws 库:WebSocket 心跳机制的模块化封装

在 WebSocket 通信中,心跳机制用于维持连接活性并检测失效连接。以下是基于 ws 库的模块化实现方案:

1. 心跳模块设计
// heartbeat.js
class Heartbeat {
  /**
   * @param {WebSocket} ws - WebSocket 实例
   * @param {object} options - 配置项
   * @param {number} options.interval - 心跳间隔(毫秒),默认 30000
   * @param {number} options.timeout - 超时阈值(毫秒),默认 10000
   * @param {string} options.pingMsg - 心跳请求消息,默认 'HEARTBEAT_PING'
   * @param {string} options.pongMsg - 心跳响应消息,默认 'HEARTBEAT_PONG'
   */
  constructor(ws, options = {}) {
    this.ws = ws;
    this.interval = options.interval || 30000;
    this.timeout = options.timeout || 10000;
    this.pingMsg = options.pingMsg || 'HEARTBEAT_PING';
    this.pongMsg = options.pongMsg || 'HEARTBEAT_PONG';
    this.pingTimer = null;
    this.pongTimeout = null;
    this.isAlive = false;

    this.start();
  }

  start() {
    this.isAlive = true;
    
    // 监听消息响应
    this.ws.on('message', (data) => {
      if (data.toString() === this.pongMsg) {
        this.reset();
      }
    });

    // 启动心跳检测
    this.pingTimer = setInterval(() => {
      if (!this.isAlive) {
        return this.terminate();
      }
      this.isAlive = false;
      this.sendPing();
      this.setPongTimeout();
    }, this.interval);
  }

  sendPing() {
    if (this.ws.readyState === this.ws.OPEN) {
      this.ws.send(this.pingMsg);
    }
  }

  setPongTimeout() {
    this.pongTimeout = setTimeout(() => {
      this.terminate();
    }, this.timeout);
  }

  reset() {
    this.isAlive = true;
    clearTimeout(this.pongTimeout);
  }

  terminate() {
    clearInterval(this.pingTimer);
    clearTimeout(this.pongTimeout);
    if (this.ws.readyState === this.ws.OPEN) {
      this.ws.terminate();
    }
  }
}

module.exports = Heartbeat;

2. 服务端集成示例
// server.js
const WebSocket = require('ws');
const Heartbeat = require('./heartbeat');

const wss = new WebSocket.Server({ port: 8080 });

wss.on('connection', (ws) => {
  // 初始化心跳检测
  const heartbeat = new Heartbeat(ws, {
    interval: 25000,
    timeout: 8000
  });

  // 处理业务消息
  ws.on('message', (message) => {
    if (message.toString() === 'HEARTBEAT_PING') {
      // 响应心跳包
      ws.send('HEARTBEAT_PONG');
    } else {
      // 处理业务数据
      console.log(`Received: ${message}`);
    }
  });

  // 连接关闭时清理
  ws.on('close', () => {
    heartbeat.terminate();
  });
});

3. 客户端集成示例
// client.js
const WebSocket = require('ws');
const Heartbeat = require('./heartbeat');

const ws = new WebSocket('ws://localhost:8080');

ws.on('open', () => {
  // 启动心跳检测
  const heartbeat = new Heartbeat(ws, {
    pingMsg: 'CLIENT_PING',
    pongMsg: 'SERVER_PONG'
  });

  // 响应服务端心跳
  ws.on('message', (data) => {
    if (data.toString() === 'HEARTBEAT_PING') {
      ws.send('HEARTBEAT_PONG');
    }
  });
});

4. 机制说明
  1. 心跳周期
    每 25 秒发送 PING 消息,等待 8 秒内响应 $$ T_{\text{cycle}} = \Delta t_{\text{interval}} + \Delta t_{\text{timeout}} $$

  2. 状态检测

    • 成功收到 PONG 时重置 isAlive 标志
    • 超时未响应时调用 terminate() 关闭连接
  3. 错误处理

    • 自动检测连接状态(readyState === OPEN
    • 清除所有定时器避免内存泄漏
5. 优化建议
  • 动态调整:根据网络状况动态计算心跳间隔
  • 重连机制:在 terminate() 后触发重连逻辑
  • 日志监控:添加调试日志记录心跳状态变化

此封装实现了:

  • 心跳检测与超时处理的解耦
  • 配置参数的可定制化
  • 与业务逻辑的隔离
  • 完整的资源清理机制
Logo

Agent 垂直技术社区,欢迎活跃、内容共建。

更多推荐