SSE 流式传输

849 阅读3分钟

一、SSE 简介

Server-Sent Events 是 HTML5 中定义的一种服务器推送技术,它基于 HTTP 协议,实现了服务器向客户端单向推送数据的功能。与传统的轮询方式相比,SSE 能够更高效地实时更新数据,减少了不必要的网络请求和延迟。

Server-Sent Events(SSE)是一种基于 HTTP 协议的单向通信技术,允许服务器向客户端推送实时更新。SSE 通过保持 HTTP 连接并持续发送数据来实现实时通信,适用于需要频繁更新但不需要客户端频繁响应的场景。

二、工作原理

  1. 客户端创建一个 EventSource 对象,并指定要连接的服务器端 URL。
  2. 服务器端在响应中设置特定的头信息,包括 Content-Type 为 text/event-stream,以及其他相关的缓存控制头。
  3. 服务器可以随时向客户端发送数据,数据以事件的形式发送,每个事件包含数据和事件类型。
  4. 客户端通过 EventSource 对象的 onmessage 回调函数接收服务器发送的事件,并进行相应的处理。

三、示例代码

客户端实现

<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Disabled Input Placeholder</title>
  <style>

  </style>
</head>

<body>

  <script>
    if (typeof (EventSource) !== "undefined") {
      var source = new EventSource("http://127.0.0.1:3000/server-endpoints");

      source.onmessage = function (event) {
        console.log("New message: " + event.data);
      };

      source.onerror = function (event) {
        console.error("Error occurred: ", event);
      };
    } else {
      console.log("SSE not supported in this browser.");
    }

  </script>

</body>

</html>

代码解读

1. 检查浏览器支持:首先检查浏览器是否支持 EventSource 对象。如果不支持,输出一条消息。

if (typeof(EventSource) !== "undefined") {

2. 创建 EventSource 实例:如果浏览器支持 EventSource,创建一个新的 EventSource 实例,传入服务器端的 SSE 端点 URL。

var source = new EventSource("http://127.0.0.1:3000/server-endpoints");

3. 处理消息事件:为 source 对象的 onmessage 事件添加一个处理函数,当服务器发送新消息时,这个函数会被调用。

source.onmessage = function (event) {
  console.log("New message: " + event.data);
};

4. 处理错误事件:为 source 对象的 onerror 事件添加一个处理函数,当发生错误时,这个函数会被调用。

source.onerror = function (event) {
  console.error("Error occurred: ", event);
};

服务端实现

const express = require('express');
const app = express();

app.get('/server-endpoints', (req, res) => {
    // 设置响应头的 Content-Type 为 text/event-stream,表示服务器发送事件流
    res.setHeader('Content-Type', 'text/event-stream');
    // 设置响应头的 Cache-Control 为 no-cache,防止缓存
    res.setHeader('Cache-Control', 'no-cache');
    // 设置响应头的 Connection 为 keep-alive,保持连接活跃
    res.setHeader('Connection', 'keep-alive');

    // 定义发送事件的函数
    const sendEvent = (data) => {
        // 以特定格式将数据写入响应流
        res.write(`data: ${JSON.stringify(data)}\n\n`);
    };

    // 模拟实时数据,每隔 1000 毫秒发送一个事件
    const intervalId = setInterval(() => {
        sendEvent({ message: 'Hello, SSE!' });
    }, 1000);

    // 监听请求的 close 事件,当客户端关闭连接时,清除定时器
    req.on('close', () => {
        clearInterval(intervalId);
    });
});

// 启动服务器,监听 3000 端口
app.listen(3000, () => {
    console.log('Server running on port 3000');
});

代码解读

1. 定义 SSE 端点:定义一个 GET 请求的处理函数,当客户端请求 /server-endpoint 时触发。。

app.get('/server-endpoint', (req, res) => {

2. 设置响应头:设置响应头以告知客户端这是一个 SSE 流。。

res.setHeader('Content-Type', 'text/event-stream');
res.setHeader('Cache-Control', 'no-cache');
res.setHeader('Connection', 'keep-alive');

(1) res.setHeader('Content-Type', 'text/event-stream');

这行代码设置了响应头的 Content-Typetext/event-stream,这是 SSE(Server-Sent Events)流的 MIME 类型。它告诉客户端(通常是浏览器),服务器将发送的是一个事件流,而不是普通的 HTML、JSON 或其他类型的内容。

  • 作用:告知客户端这是一个 SSE 流,客户端会根据这个 MIME 类型来处理接收到的数据。
  • 重要性:这是实现 SSE 的关键步骤之一,客户端需要知道如何解析和处理从服务器接收到的数据。

(2) res.setHeader('Cache-Control', 'no-cache');

这行代码设置了 Cache-Control 头为 no-cache,目的是防止浏览器或其他中间代理缓存 SSE 流。

  • 作用:确保每次请求都能从服务器获取最新的数据,而不是从缓存中读取。
  • 重要性:实时数据传输要求数据是最新的,缓存可能会导致客户端接收到过时的数据,从而影响实时性。

(3) res.setHeader('Connection', 'keep-alive');

这行代码设置了 Connection 头为 keep-alive,目的是保持 HTTP 连接不断开。

  • 作用:保持客户端和服务器之间的连接持续打开,以便服务器可以持续发送数据。
  • 重要性:SSE 依赖于持久的 HTTP 连接来推送实时数据,如果连接关闭,SSE 流就会中断。

3. 定义发送事件的函数:定义一个 sendEvent 函数,用于向客户端发送数据。数据格式为 data: <data>\n\n

const sendEvent = (data) => {
  res.write(`data: ${JSON.stringify(data)}\n\n`);
};

4. 模拟实时数据:使用 setInterval 模拟每秒发送一次数据给客户端。

const intervalId = setInterval(() => {
  sendEvent({ message: 'Hello, SSE!' });
}, 1000);

5. 处理连接关闭:当客户端关闭连接时,清除定时器以释放资源。

req.on('close', () => { clearInterval(intervalId); });

四、SSE 的优缺点

优点

  1. 简单易用:SSE 使用标准的 HTTP 协议,客户端和服务器端的实现都非常简单。
  2. 自动重连EventSource 对象会在连接断开时自动尝试重连。
  3. 轻量级:相比 WebSocket,SSE 更加轻量级,适合频繁但数据量较小的更新。

缺点

  1. 单向通信:SSE 只能从服务器向客户端推送数据,无法实现双向通信。
  2. 浏览器兼容性:虽然大多数现代浏览器都支持 SSE,但仍有一些老旧浏览器不支持。
  3. 连接数限制:由于 SSE 基于 HTTP 协议,浏览器对同一域名的并发连接数有限制。

五、适用场景

SSE 非常适合以下场景:

  1. 实时通知:如新闻网站的实时更新、社交媒体的通知等。
  2. 数据流:如股票市场的实时数据、物联网设备的数据流等。
  3. 日志监控:如服务器日志的实时监控、应用程序的实时日志等。

参考文章:juejin.cn/post/740473…