SSE(Server-Sent Events) 如何解决心跳超时问题?

什么是 SSE?

SSE(Server-Sent Events) 是一种在 Web 浏览器和服务器之间实现实时通信的技术,它允许服务端向客户端推送数据。

与传统的轮询不同,SSE 是基于 HTTP 长连接实现的,因此在服务器端有数据更新时,它可以主动向客户端推送数据,不需要客户端频繁地向服务器发起请求。

SSE 通常用于实现实时通知、实时聊天等应用场景。

心跳超时问题

由于 SSE 通常使用 HTTP 长连接,因此客户端需要不断向服务器发送心跳消息以保持连接,防止长连接被服务器断开。否则,长时间没有通信的连接可能被认为是死掉的连接,从而被服务器端断开。

SSE 规范中建议客户端每隔 3 秒发送一次心跳消息,同时建议服务器在长时间没有收到心跳消息时,应该主动向客户端发送一个特殊的消息以探测是否还存活。

然而,在一些网络较差或服务器压力较大的情况下,即使按照规范发送心跳消息,仍然有可能出现连接断开的情况,导致客户端无法接收到服务端推送的实时数据。

具体来说,就是客户端发送心跳请求,但服务端在一定时间内没有收到请求,就会认为这个连接已经断开了。

如何解决这个问题呢?

解决方法

一种可行的解决方法是在客户端和服务端分别设置超时时间,当一方没有及时响应时,另一方可以认为超时了,从而主动断开连接。

具体来说,客户端发送心跳请求时,可以设置一个超时时间,如果在该时间内没有收到服务端响应,就认为连接已经断开,并重新连接。服务端也可以设置一个类似的超时时间,当有客户端连接时,设置一个超时时间,在该时间内没有收到客户端的心跳请求,就主动断开该连接。这样可以避免死连接的情况出现。

以下是一份示例代码:

客户端

const eventSource = new EventSource('/sse');
let timeoutId;

eventSource.addEventListener('open', e => {
  console.log('连接已打开');
  resetTimeout();
})

eventSource.addEventListener('message', e => {
  console.log('收到数据:', e.data);
  resetTimeout();
})

eventSource.addEventListener('error', e => {
  console.log('连接出错:', e);
  resetTimeout();
})

function resetTimeout() {
  clearTimeout(timeoutId);
  timeoutId = setTimeout(() => {
    console.log('与服务器连接已超时,正在重连...');
    eventSource.close();
    connect();
  }, 10000);
}

function connect() {
  eventSource.open();
}

服务端

const http = require('http');

const TIMEOUT = 20000; // 超时时间为 20s

http.createServer((req, res) => {
  if (req.url === '/sse') {
    res.writeHead(200, {
      'Content-Type': 'text/event-stream',
      // 设置超时时间
      'Cache-Control': 'no-cache',
      'Connection': 'keep-alive',
      'Access-Control-Allow-Origin': '*'
    });

    // 初始化连接
    res.write(':ok\n\n');

    let timeoutId;

    req.on('close', () => {
      clearTimeout(timeoutId);
    })

    req.socket.setTimeout(TIMEOUT);

    req.socket.on('timeout', () => {
      console.log('连接超时,断开连接');
      req.socket.end();
    })

    timeoutId = setInterval(() => {
      console.log('发送心跳包');
      res.write(':heartbeat\n\n');
    }, 3000);
  } else {
    res.writeHead(404);
    res.end();
  }
}).listen(3000, () => {
  console.log('服务启动,监听 3000 端口');
})

总结

SSE 是一种实现实时通信的技术,但长时间没有通信的连接可能会被服务器端断开,导致客户端无法接收到实时数据。

为了避免这个问题,可以在客户端和服务端分别设置超时时间,当一方没有及时响应时,另一方可以认为超时了,从而主动断开连接。

以上是一份解决心跳超时问题的示例代码,可以参考使用。

来源:JavaScript中文网 ,转载请注明来源 本文地址:https://www.javascriptcn.com/post/65b328e1add4f0e0ffc3a290