Server-Sent Events(SSE)底层推送机制

570 阅读4分钟

Server-Sent Events(SSE)底层推送机制

SSE(Server-Sent Events)的底层推送机制是基于 HTTP 协议的长连接实现的。它的核心原理是通过保持 HTTP 连接打开,服务器可以持续向客户端发送数据流。以下是 SSE 底层实现推送的细节与原理的深入解析:


1. 底层协议与通信机制

(1)HTTP 长连接
  • SSE 基于 HTTP/1.1 的长连接(Keep-Alive)特性,服务器在响应客户端请求后,不会立即关闭连接,而是保持连接打开。
  • 客户端通过 EventSource API 发起一个普通的 HTTP GET 请求,服务器通过设置响应头 Content-Type: text/event-stream 来标识这是一个 SSE 连接。
(2)事件流格式
  • 服务器发送的数据必须遵循 SSE 的特定格式:
    • 每行以 field: value 的形式发送。
    • 消息以两个换行符 \n\n 结束。
    • 常用字段:
      • data: 消息内容(可以有多行)。
      • event: 自定义事件名称。
      • id: 消息的唯一标识符。
      • retry: 客户端重连时间(毫秒)。

示例:

event: customEvent
data: 这是一条自定义事件消息
id: 12345
retry: 5000

data: 这是一条普通消息

2. 推送的底层实现细节

(1)客户端发起请求
  • 客户端通过 EventSource API 发起一个 HTTP GET 请求:
    const eventSource = new EventSource('/sse-endpoint');
    
  • 这个请求会携带标准的 HTTP 头信息,服务器通过解析请求头来识别 SSE 连接。
(2)服务器响应并保持连接
  • 服务器接收到请求后,设置响应头并保持连接打开:
    HTTP/1.1 200 OK
    Content-Type: text/event-stream
    Cache-Control: no-cache
    Connection: keep-alive
    
  • 服务器不会关闭连接,而是通过这个连接持续发送数据。
(3)服务器推送消息
  • 服务器通过保持的连接,向客户端发送符合 SSE 格式的消息。
  • 每条消息以 field: value 的形式发送,并以两个换行符 \n\n 结束。
  • 示例(Node.js):
    res.write('data: 这是一条消息\n\n');
    res.write('event: customEvent\n');
    res.write('data: 这是一条自定义事件消息\n\n');
    
(4)客户端接收消息
  • 客户端通过 EventSource API 监听服务器发送的消息:
    eventSource.onmessage = function(event) {
        console.log('收到消息:', event.data);
    };
    eventSource.addEventListener('customEvent', function(event) {
        console.log('自定义事件:', event.data);
    });
    

3. 底层推送的原理

(1)TCP 连接保持
  • SSE 依赖于底层的 TCP 连接。服务器和客户端建立 TCP 连接后,服务器通过这个连接持续发送数据。
  • 由于 TCP 连接是双向的,SSE 利用了这个特性来实现服务器到客户端的单向数据流。
(2)数据流的分帧
  • SSE 的消息是通过 HTTP 响应的数据流(stream)发送的。每条消息以 \n\n 分隔,客户端通过解析这些分隔符来识别每条消息。
  • 服务器可以随时向流中写入数据,客户端会实时接收并处理这些数据。
(3)心跳机制
  • 为了防止连接超时或被代理服务器关闭,服务器可以定期发送心跳消息(如空行或注释):
    setInterval(() => {
        res.write(': 心跳\n\n'); // 发送心跳消息
    }, 30000); // 每 30 秒发送一次
    

4. 连接管理与优化

(1)客户端重连
  • 如果客户端断开连接,EventSource 会自动尝试重新连接。
  • 服务器可以通过 retry 字段指定客户端重连的时间间隔:
    retry: 5000
    
(2)连接状态管理
  • 服务器需要维护所有客户端的连接对象,并在客户端断开时清理资源。
  • 示例(Node.js):
    const clients = new Map(); // 存储客户端连接
    
    app.get('/sse-endpoint', (req, res) => {
        const clientId = req.query.clientId; // 从 URL 参数获取客户端 ID
        clients.set(clientId, res); // 存储连接
    
        req.on('close', () => {
            clients.delete(clientId); // 清理断开连接的客户端
        });
    });
    
(3)批量推送
  • 如果需要向多个客户端推送相同消息,可以遍历所有连接对象并发送消息:
    function broadcast(message) {
        clients.forEach((res) => {
            res.write(`data: ${JSON.stringify(message)}\n\n`);
        });
    }
    

5. 安全性考虑

  • 身份验证:在客户端订阅 SSE 时,服务器应验证客户端的身份(如通过 Token 或 Session)。
  • 跨域问题:如果客户端和服务器不在同一域名下,需要设置 CORS 头部(如 Access-Control-Allow-Origin)。
  • 数据加密:使用 HTTPS 加密通信,防止数据被窃听或篡改。

6. 总结

SSE 的底层推送机制基于 HTTP 长连接和事件流格式,通过保持 TCP 连接打开,服务器可以持续向客户端发送数据。它的核心原理是通过 HTTP 协议实现服务器到客户端的单向通信,结合心跳机制和连接管理,确保通信的可靠性和实时性。通过合理设计,SSE 可以高效地实现服务器向客户端的消息推送。