Server-Sent Events (SSE) 指南

1,292 阅读7分钟

1. 什么是 SSE?

Server-Sent Events (SSE) 是一种允许服务器向客户端推送数据的 Web 技术:

  • 它建立在 HTTP 协议之上,是一种单向通信机制(服务器→客户端)
  • 使用持久化的 HTTP 连接实现实时数据推送
  • 数据格式为 UTF-8 编码的文本消息
  • 浏览器原生支持,使用 EventSource API

2. SSE vs 其他实时通信技术

2.1 Client Polling

  • 客户端定期轮询服务器
  • 实现简单但效率低
  • 即使没有更新也会发送请求

2.2 WebSocket

  • 双向通信
  • 需要额外的协议支持
  • 实现相对复杂

2.3 SSE的优势

  • 基于 HTTP,实现简单
  • 自动重连机制
  • 原生事件 ID 跟踪
  • 标准化的消息格式

3. 适用场景

SSE 特别适合以下应用场景:

  • 实时股票行情
  • 体育比分直播
  • 新闻推送
  • 系统通知
  • 物联网数据推送
  • 电商实时信息
  • 监控系统

4. 技术实现

4.1 服务器端实现

服务器需要:

Content-Type: text/event-stream
Cache-Control: no-cache
Connection: keep-alive

消息格式:

id: <事件ID>
event: <事件类型>
data: <事件数据>
retry: <重连时间>

4.2 客户端实现

const eventSource = new EventSource('http://localhost:3000/events');

// 处理连接打开
eventSource.onopen = (event) => {
    console.log('Connection opened');
};

// 处理消息
eventSource.onmessage = (event) => {
    const data = JSON.parse(event.data);
    console.log('Received:', data);
};

// 处理错误
eventSource.onerror = (error) => {
    console.error('EventSource failed:', error);
    eventSource.close();
};

// 监听自定义事件
eventSource.addEventListener('customEvent', (event) => {
    console.log('Custom event:', event.data);
});

4.3 Node.js SSE 实现示例

// 创建Express应用实例
const app = express();

// SSE(服务器发送事件)端点
app.get('/events', (req, res) => {
    // 设置SSE所需的HTTP头
    res.setHeader('Content-Type', 'text/event-stream');
    res.setHeader('Cache-Control', 'no-cache');
    res.setHeader('Connection', 'keep-alive');

    // 每2秒发送一条普通消息
    const timeIntervalId = setInterval(() => {
        const data = {
            time: new Date().toLocaleTimeString(),
            message: 'Server time update'
        };
        res.write(`data: ${JSON.stringify(data)}\n\n`);
    }, 2000);

    // 每5秒发送一条自定义事件消息
    const customIntervalId = setInterval(() => {
        const customData = {
            time: new Date().toLocaleTimeString(),
            message: 'This is a custom event!'
        };
        // 发送带有自定义事件类型的SSE数据
        res.write(`event: customEvent\n`);
        res.write(`data: ${JSON.stringify(customData)}\n\n`);
    }, 5000);

    // 当客户端断开连接时清理所有定时器
    req.on('close', () => {
        clearInterval(timeIntervalId);
        clearInterval(customIntervalId);
    });
});

5. 注意事项与限制

5.1 主要限制

  • 仅支持文本数据(UTF-8),不支持二进制,消息格式必须符合 SSE 规范(如结尾需要 \n\n)
  • 浏览器对同一域名有 TCP 连接数限制(通常为 6-8 个),HTTP1.1每个 SSE 连接会占用一个 TCP 连接,当达到最大连接数时,新的 SSE 连接将被阻塞或失败。可以使用HTTP2.0支持多路复用,单个 TCP 连接可处理多个 SSE 流
  • IE不支持SSE,需要考虑降级方案(如 long polling),主流现代浏览器(Chrome、Firefox、Safari、Edge)都支持

5.2 最佳实践

  • 实现错误处理和重试机制
  • 合理设置超时和重连间隔
  • 考虑服务器负载能力
  • 在需要时正确关闭连接

参考资料

根据搜索结果,我来为您总结一份关于Server-Sent Events (SSE)的详细教程:

1. 什么是 SSE?

Server-Sent Events (SSE) 是一种允许服务器向客户端推送数据的 Web 技术:

  • 它建立在 HTTP 协议之上,是一种单向通信机制(服务器→客户端)
  • 使用持久化的 HTTP 连接实现实时数据推送
  • 数据格式为 UTF-8 编码的文本消息
  • 浏览器原生支持,使用 EventSource API

2. SSE vs 其他实时通信技术

2.1 Client Polling

  • 客户端定期轮询服务器
  • 实现简单但效率低
  • 即使没有更新也会发送请求

2.2 WebSocket

  • 双向通信
  • 需要额外的协议支持
  • 实现相对复杂

2.3 SSE的优势

  • 基于 HTTP,实现简单
  • 自动重连机制
  • 原生事件 ID 跟踪
  • 标准化的消息格式

3. 适用场景

SSE 特别适合以下应用场景:

  • 实时股票行情
  • 体育比分直播
  • 新闻推送
  • 系统通知
  • 物联网数据推送
  • 电商实时信息
  • 监控系统

4. 技术实现

4.1 服务器端实现

服务器需要:

Content-Type: text/event-stream
Cache-Control: no-cache
Connection: keep-alive

消息格式:

id: <事件ID>
event: <事件类型>
data: <事件数据>
retry: <重连时间>

4.2 客户端实现

基本用法:

const evtSource = new EventSource("/api/events");

evtSource.onmessage = (event) => {
  console.log(event.data);
};

// 监听特定事件
evtSource.addEventListener("custom-event", (e) => {
  console.log(e.data);
});

4.3 Spring WebFlux 实现示例

@GetMapping(path = "/events", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
public Flux<ServerSentEvent<String>> streamEvents() {
    return Flux.interval(Duration.ofSeconds(1))
        .map(sequence -> ServerSentEvent.<String>builder()
            .id(String.valueOf(sequence))
            .event("periodic-event")
            .data("SSE - " + LocalTime.now().toString())
            .build());
}

5. 注意事项与限制

5.1 主要限制

  • 仅支持文本数据(UTF-8),不支持二进制
  • 默认每个浏览器限制6个并发SSE连接
  • IE不支持SSE

5.2 最佳实践

  • 实现错误处理和重试机制
  • 合理设置超时和重连间隔
  • 考虑服务器负载能力
  • 在需要时正确关闭连接

6. 调试与测试

  • 使用浏览器开发者工具监控SSE连接
  • 测试断线重连机制
  • 验证消息格式正确性
  • 压力测试并发连接数

参考资料

QA

Q:什么是服务器发送事件(SSE)?它的核心特点是什么?

A:SSE 是一种允许服务器向浏览器推送实时更新的技术。核心特点包括单向通信、基于 HTTP 协议、自动重连和事件流格式。

Q:SSE 与 WebSocket 有什么区别?请列出至少三个主要区别。

A:

1️⃣ SSE 是单向通信,WebSocket 是双向通信。

2️⃣ SSE 使用 HTTP 协议,WebSocket 使用 ws/wss 协议。

3️⃣ SSE 更适合简单的服务器推送,WebSocket 更适合复杂的实时交互。

4️⃣ SSE 只支持文本,WebSocket 支持文本和二进制

5️⃣ SSE 原生支持自动重连,WebSocket 需要手动实现

Q:使用 SSE 通信的优点和局限性分别是什么?

A:**优点:**简单易用、自动重连、支持事件流。**局限性:**仅支持单向通信、受限于 HTTP 连接数、可能被代理拦截。

Q:如何在前端实现 SSE?请用代码展示如何订阅 SSE 消息。

A:

     const eventSource = new EventSource('your-server-url');
     eventSource.onmessage = function(event) {
         console.log('Message:', event.data);
     };

Q:SSE 的 Content-Type 为什么必须是 text/event-stream?

A:这是 SSE 规范要求,text/event-stream 是 SSE 专用的 MIME 类型,它告知浏览器这是一个事件流,

Q:在后端如何实现 SSE?请用任意后端语言描述实现过程(如 Node.js、Python)。

A:笔记中有代码

Q:SSE 是单向通信还是双向通信?如何结合其他技术实现双向通信?

A:SSE 是单向通信,浏览器从服务器接收数据,而客户端不能直接通过同一连接发送数据回服务器。如果需要双向通信,可以结合 WebSocket 技术来实现

Q:如何处理 SSE 连接的自动重连?浏览器的默认重连策略是什么?

A:**浏览器有内建的重连机制:**如果 SSE 连接丢失,它会自动重新连接,默认的重连时间是 3秒。服务端也可以通过 retry 字段来指定自定义的重连时间(单位是毫秒)。例如:

    res.write('retry: 5000\n'); // 设置重连时间为 5 秒

**客户端自动重连流程:**如果需要自定义重连机制,客户端可以监听 onerror 事件,并手动重新创建 EventSource 实例:

    const eventSource = new EventSource('/events');

    eventSource.onerror = function () {
        console.error('Connection lost, reconnecting...');
        eventSource.close();  // 关闭旧连接
        const newEventSource = new EventSource('/events');  // 创建新连接
    };

Q:如果有多个 SSE 客户端连接到服务器,如何管理这些连接?

A:服务端可以维护一个连接列表,并循环给每个连接推送数据。这是通过将每个客户端连接的 res 对象存储在一个数组或对象中来实现的:

    let clients = [];

    app.get('/events', (req, res) => {
        res.setHeader('Content-Type', 'text/event-stream');
        res.setHeader('Cache-Control', 'no-cache');
        res.setHeader('Connection', 'keep-alive');
        
        // 将当前连接保存到客户端列表中
        clients.push(res);

        // 每隔1秒广播消息给所有客户端
        const intervalId = setInterval(() => {
            const message = { text: 'Broadcast message', timestamp: new Date() };
            clients.forEach(client => client.write(`data: ${JSON.stringify(message)}\n\n`));
        }, 1000);

        // 客户端断开时清除其连接
        req.on('close', () => {
            clients = clients.filter(client => client !== res);
            clearInterval(intervalId);
        });
    });

Q:SSE 的事件流数据格式有什么要求?如果想自定义事件类型,应该如何处理?

每条消息以 data: 开头,后面跟随实际的消息内容。每条消息以两个换行符(\n\n)结束。如果要使用自定义事件类型,可以使用 event: 字段指定:

    res.write('event: customEvent\n');
    res.write('data: {"message": "Hello, custom event"}\n\n');