HTTP SSE 入门:EventSource API 使用指南 – wiki词典

“`markdown

HTTP SSE 入门:EventSource API 使用指南

在现代 Web 应用中,实时性是一个越来越重要的需求。从即时聊天、实时通知到仪表盘更新,用户期望数据能够及时、自动地推送到他们的浏览器。虽然 WebSocket 是实现双向实时通信的强大工具,但对于服务端仅需单向推送数据到客户端的场景,HTTP Server-Sent Events (SSE) 提供了一个更简单、基于 HTTP 的高效解决方案。

本文将深入探讨 HTTP SSE 的基本概念,并重点介绍如何在客户端使用原生的 EventSource API 来接收服务端事件。

什么是 Server-Sent Events (SSE)?

Server-Sent Events 是一种允许服务器通过 HTTP 连接单向发送数据更新到客户端的技术。与传统的 HTTP 请求-响应模型不同,SSE 允许服务器保持连接打开,并在数据可用时将新的事件“推送”到客户端。

SSE 的主要特点:

  • 单向通信: 数据流从服务器到客户端。客户端不能通过同一个 SSE 连接发送数据给服务器。
  • 基于 HTTP: SSE 建立在标准的 HTTP/1.1 协议之上,这意味着它可以通过现有的 HTTP 基础设施(如代理、防火墙)工作,并且不需要特殊的协议或端口。
  • 持久连接: 服务器会保持连接打开,以便随时发送新的事件。
  • 自动重连: 客户端的 EventSource API 内置了自动重连机制,当连接中断时会自动尝试重新建立连接。
  • 简单易用: 相较于 WebSocket,SSE 的 API 更为简单,特别适用于那些只需要服务端推送更新的场景。
  • 数据格式: 事件数据以简单的文本流格式发送,每条消息以 data: 开头,以两个换行符 \n\n 结束。

SSE 与 WebSocket 的对比:

特性 SSE WebSocket
通信方向 单向 (服务器 -> 客户端) 双向 (服务器 <-> 客户端)
协议 基于 HTTP 独立的 WebSocket 协议 (WS/WSS)
复杂性 更简单,特别适合单向推送 更复杂,适合需要双向交互的场景
自动重连 内置支持 需要手动实现
代理/防火墙 友好 (使用标准 HTTP) 可能需要特殊配置
数据类型 仅文本 (UTF-8) 文本和二进制数据

EventSource API:客户端的 SSE 利器

在浏览器中,JavaScript 提供了一个内置的 EventSource 接口,用于方便地连接到 SSE 流并处理接收到的事件。

创建 EventSource 实例

要开始使用 SSE,首先需要创建一个 EventSource 对象,并传入 SSE 服务器端点的 URL。

javascript
const eventSource = new EventSource('/stream');
// 假设你的服务器在 '/stream' 路径上提供 SSE 服务

EventSource 构造函数还可以接受第二个可选参数,一个 EventSourceInit 对象,用于配置凭据模式(withCredentials)。

javascript
const eventSource = new EventSource('/stream', { withCredentials: true });
// 如果需要发送跨域请求并包含 cookie 或 HTTP 认证凭据,请设置为 true

监听事件

EventSource 对象继承了 EventTarget 接口,这意味着你可以使用 addEventListener() 方法来监听不同类型的事件。

1. message 事件 (默认事件)

这是最常见的事件类型。当服务器发送不带 event: 字段的消息时,或者显式地发送 event: message 时,会触发此事件。

javascript
eventSource.addEventListener('message', function(event) {
console.log('接收到消息:', event.data);
// event.data 包含了服务器发送的数据
// event.lastEventId 包含了服务器发送的 'id:' 字段的值 (如果存在)
});

2. 自定义命名事件

SSE 允许服务器发送带有 event: 字段的命名事件。这使得客户端可以根据事件类型来注册不同的处理器。

服务器响应示例:

“`
event: user_joined
data: {“id”: 101, “username”: “Alice”}

event: new_message
data: {“from”: “Alice”, “content”: “Hello everyone!”}
id: 123
“`

客户端监听:

“`javascript
eventSource.addEventListener(‘user_joined’, function(event) {
console.log(‘新用户加入:’, JSON.parse(event.data));
});

eventSource.addEventListener(‘new_message’, function(event) {
console.log(‘收到新消息:’, JSON.parse(event.data), ‘消息ID:’, event.lastEventId);
});
“`

3. open 事件 (连接建立)

当与服务器的连接成功建立时,会触发 open 事件。

javascript
eventSource.addEventListener('open', function(event) {
console.log('SSE 连接已建立。');
});

4. error 事件 (连接错误)

当连接发生错误(例如,网络问题、服务器响应非 200 状态码)时,会触发 error 事件。

javascript
eventSource.addEventListener('error', function(event) {
if (event.readyState === EventSource.CLOSED) {
console.error('SSE 连接已关闭。');
} else {
console.error('SSE 连接错误:', event);
}
});

控制重连行为

SSE 的一个强大之处在于其内置的自动重连机制。当连接中断时,浏览器会根据服务器发送的 retry: 字段指定的间隔(默认为 3 秒)自动尝试重新连接。如果服务器没有发送 retry: 字段,或者发送了无效值,浏览器将使用默认的 3 秒间隔。

关闭连接

当不再需要接收事件时,应该显式地关闭 EventSource 连接,以释放资源。

javascript
eventSource.close();
console.log('SSE 连接已关闭。');

SSE 服务器端示例 (以 Node.js Express 为例)

为了让客户端能够接收 SSE 事件,服务器需要以特定的方式响应 HTTP 请求。

  1. 设置 Content-Type: 必须设置为 text/event-stream
  2. 设置 Cache-ControlConnection 头: 通常设置为 no-cachekeep-alive,以确保连接不会被代理缓存,并保持持久。
  3. 发送事件数据: 每条事件消息都遵循特定的格式,以 data: 开头,并以 \n\n 结束。

“`javascript
// server.js (使用 Express)
const express = require(‘express’);
const app = express();
const PORT = 3000;

app.get(‘/stream’, (req, res) => {
// 1. 设置 SSE 相关的响应头
res.setHeader(‘Content-Type’, ‘text/event-stream’);
res.setHeader(‘Cache-Control’, ‘no-cache’);
res.setHeader(‘Connection’, ‘keep-alive’);
// 如果需要跨域访问,可以设置 CORS 头
res.setHeader(‘Access-Control-Allow-Origin’, ‘*’);

// 2. 发送初始数据或连接成功消息
res.write('data: 连接成功!\n\n');

let counter = 0;
const intervalId = setInterval(() => {
    counter++;
    // 3. 发送命名事件
    res.write(`event: update\n`);
    res.write(`data: {"message": "这是来自服务器的更新 #${counter}", "timestamp": "${new Date().toISOString()}"}\n`);
    // 4. 设置消息 ID,用于客户端重连后告诉服务器从哪个 ID 开始发送
    res.write(`id: ${counter}\n`);
    // 5. 设置重连间隔 (可选,客户端默认 3 秒)
    // res.write(`retry: 5000\n`); // 客户端在 5 秒后重连
    res.write('\n'); // 每条消息必须以两个换行符结束

    // 模拟断开连接
    if (counter === 5) {
        clearInterval(intervalId);
        res.end(); // 结束响应,模拟连接断开
        console.log('服务器断开 SSE 连接。');
    }
}, 2000); // 每 2 秒发送一次更新

// 当客户端断开连接时,清理资源
req.on('close', () => {
    console.log('客户端断开 SSE 连接。');
    clearInterval(intervalId);
    res.end();
});

});

app.listen(PORT, () => {
console.log(SSE server listening on port ${PORT});
});
“`

结论

HTTP Server-Sent Events 提供了一种优雅而简单的方式,用于实现从服务器到客户端的单向实时数据推送。通过原生的 EventSource API,前端开发者可以轻松地连接到 SSE 流,监听并处理来自服务器的事件。

尽管 WebSocket 在双向通信场景中是不可替代的,但对于诸如新闻更新、股票行情、直播点赞数、通知等仅需服务器主动推送数据的应用,SSE 因其基于 HTTP、简单、自动重连的特性而成为一个极具吸引力的选择。理解并掌握 SSE,将为你的 Web 应用增添强大的实时交互能力。
“`

滚动至顶部