37.讲一下 SSE
什么是 SSE
SSE(Server-Sent Events)是一种服务器推送技术,服务器向客户端推送数据而不需要客户端做任何处理。
SSE 的核心特点
- 单向通信:仅支持服务器向客户端推送数据(客户端无法反向发送消息)。 
- 基于 HTTP:无需复杂协议,兼容现有 HTTP 基础设施(如身份验证、CORS)。 
- 自动重连:内置断线重连机制,客户端会自动尝试重新连接。 
- 文本协议:默认传输文本数据(如 JSON),也可通过二进制编码传输其他格式。 
- 轻量高效:相比 WebSocket,SSE 实现更简单,适合低频次数据推送场景。 
使用场景
- 实时仪表盘(如股票价格、监控数据)。 
- 新闻/社交媒体动态更新。 
- 长任务进度通知(如文件上传、数据处理进度)。 
- 简单实时聊天(仅需单向推送时)。 
SSE 与 WebSocket 的区别
- SSE 是单向通信,只能服务器向客户端推送,WebSocket 是双向通信,允许服务器与客户端进行全双工通信。
- SSE 属于轻量级,使用简单;WebSocket 协议相对复杂。
- SSE 默认支持断线重连,WebSocket 需要自己实现。
- SSE 一般只用来传送文本,二进制数据需要编码后传送,WebSocket 默认支持传送二进制数据。
- SSE 支持自定义发送的消息类型。
| 特性 | SSE | WebSocket | 
|---|---|---|
| 通信方向 | 单向(服务器 → 客户端) | 全双工(双向通信) | 
| 协议 | HTTP(长连接) | 独立的 ws:// 或 wss:// 协议 | 
| 复杂度 | 简单,无需额外库 | 需处理握手、帧协议等 | 
| 二进制支持 | 需通过 Base64 编码 | 原生支持 | 
| 适用场景 | 低频次服务器推送 | 高频次双向交互(如游戏、聊天) | 
| 浏览器兼容性 | 除 IE 外主流支持 | 广泛支持 | 
客户端实现(浏览器端)
使用 EventSource API 监听服务器推送:
// 创建 EventSource 连接(指向服务器端点)
const eventSource = new EventSource("/sse-endpoint");
// 监听默认事件(未指定事件类型时触发)
eventSource.onmessage = (event) => {
  const data = JSON.parse(event.data);
  console.log("收到数据:", data);
};
// 监听自定义事件(如 'status-update')
eventSource.addEventListener("status-update", (event) => {
  console.log("状态更新:", event.data);
});
// 处理错误
eventSource.onerror = (error) => {
  console.error("SSE 连接异常:", error);
  // 可在此处尝试手动重连
};
服务端实现(示例)
以 Node.js(Express)为例,需设置响应头并持续发送数据流:
app.get("/sse-endpoint", (req, res) => {
  // 设置 SSE 响应头
  res.setHeader("Content-Type", "text/event-stream");
  res.setHeader("Cache-Control", "no-cache");
  res.setHeader("Connection", "keep-alive");
  // 定期推送数据
  let counter = 0;
  const intervalId = setInterval(() => {
    counter++;
    // 发送数据格式:data: { ... }\n\n(注意换行符)
    res.write(
      `data: ${JSON.stringify({ time: Date.now(), count: counter })}\n\n`
    );
    // 自定义事件示例
    // res.write('event: status-update\ndata: "Processing..."\n\n');
    // 终止条件(如 10 次后关闭)
    if (counter >= 10) {
      clearInterval(intervalId);
      res.end(); // 关闭连接
    }
  }, 1000);
  // 客户端断开连接时清理
  req.on("close", () => {
    clearInterval(intervalId);
    res.end();
  });
});
