“`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 基础设施(如代理、防火墙)工作,并且不需要特殊的协议或端口。
- 持久连接: 服务器会保持连接打开,以便随时发送新的事件。
- 自动重连: 客户端的
EventSourceAPI 内置了自动重连机制,当连接中断时会自动尝试重新建立连接。 - 简单易用: 相较于 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 请求。
- 设置
Content-Type: 必须设置为text/event-stream。 - 设置
Cache-Control和Connection头: 通常设置为no-cache和keep-alive,以确保连接不会被代理缓存,并保持持久。 - 发送事件数据: 每条事件消息都遵循特定的格式,以
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 应用增添强大的实时交互能力。
“`