主题
SSE(Server-Sent Events)
介绍
SSE(Server-Sent Events) 是一种服务器向客户端单向推送数据的技术。
- 基于 HTTP 协议:不需要像 WebSocket 那样建立独立连接。
- 单向通信:只能服务器推送给客户端,客户端不能通过 SSE 发送消息。
- 自动重连:断线后浏览器会自动尝试重连。
- 轻量级:比 WebSocket 简单,适合只需要服务器推送的场景。
客户端实现
EventSource API(标准 SSE)
适用于简单的服务器推送场景,浏览器原生支持。
优点:
- ✅ 浏览器原生支持,API 简单
- ✅ 自动重连机制
- ✅ 自动解析 SSE 格式
限制:
- ❌ 只能发送 GET 请求
- ❌ 不能自定义请求头(如 Authorization)
- ❌ 不能发送请求体
适用场景:
- 服务器主动推送通知
- 实时数据更新(股票、天气等)
- 进度条更新
js
// 建立连接
const eventSource = new EventSource('/api/sse')
// 监听默认消息(服务端只发送 data: 字段)
eventSource.onmessage = (event) => {
const data = JSON.parse(event.data)
console.log('收到推送:', data)
}
// 监听自定义事件(服务端必须发送 event: notification 字段)
eventSource.addEventListener('notification', (event) => {
console.log('收到通知:', event.data)
})
// 监听另一个自定义事件(服务端必须发送 event: update 字段)
eventSource.addEventListener('update', (event) => {
console.log('收到更新:', event.data)
})
// 错误处理
eventSource.onerror = (error) => {
console.error('连接错误', error)
}
// 关闭连接
eventSource.close()readyState 状态:
source.readyState 有 3 种状态:
0:未连接。等同于EventSource.CONNECTING1:已连接。等同于EventSource.OPEN2:已关闭。等同于EventSource.CLOSED
事件类型:
内置事件(3 种):
onopen:客户端与服务器建立连接时触发onmessage:接收没有 event 字段的默认消息onerror:客户端与服务器连接出错时触发
自定义事件:
- 使用
addEventListener('eventName')监听 - 事件名必须与服务端发送的
event:字段匹配 - 示例:服务端发送
event: notification,客户端就用addEventListener('notification')
- 使用
Fetch + ReadableStream
适用于需要复杂请求配置的流式响应,如 AI API。
优点:
- ✅ 支持 POST/PUT 等方法
- ✅ 可以自定义请求头和请求体
- ✅ 更灵活的控制
限制:
- ❌ 需要手动解析 SSE 格式
- ❌ 需要手动实现重连逻辑
- ❌ 代码相对复杂
适用场景:
- OpenAI/DeepSeek 等 AI API 的流式响应
- 需要认证的流式接口
- 需要发送复杂请求体的场景
js
const API_URL = 'https://api.deepseek.com/v1/chat/completions'
const apiKey = 'your-api-key'
async function streamChat(prompt) {
const response = await fetch(API_URL, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${apiKey}`,
},
body: JSON.stringify({
model: 'deepseek-chat',
messages: [{ role: 'user', content: prompt }],
stream: true, // 启用流式传输
}),
})
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`)
}
// 读取流式响应
const reader = response.body.getReader()
const decoder = new TextDecoder('utf-8')
let buffer = ''
while (true) {
const { done, value } = await reader.read()
if (done) break
// 解码数据块
buffer += decoder.decode(value, { stream: true })
// 处理 SSE 格式的数据
const lines = buffer.split('\n')
buffer = lines.pop() || '' // 保留不完整的行
for (const line of lines) {
if (line.startsWith('data: ')) {
const data = line.slice(6)
if (data === '[DONE]') continue
try {
const json = JSON.parse(data)
const content = json.choices?.[0]?.delta?.content
if (content) {
console.log(content) // 实时输出内容
}
} catch (e) {
console.error('解析失败:', e)
}
}
}
}
}
// 使用
streamChat('解释一下什么是 SSE')关键点:
- 逐块读取:使用
reader.read()逐块读取数据 - 解码处理:用
TextDecoder将字节转为文本 - 行缓冲:处理不完整的数据行
- SSE 格式解析:手动处理
data:前缀的行
服务端实现
js
// 使用 Express 框架示例
app.get('/api/sse', (req, res) => {
// 设置 SSE 响应头
res.setHeader('Access-Control-Allow-Origin', '*') // 允许跨域
res.setHeader('Content-Type', 'text/event-stream') // 告诉客户端发送的是数据流
res.setHeader('Cache-Control', 'no-cache') // 告诉客户端不要缓存数据
res.setHeader('Connection', 'keep-alive') // 长连接
// 发送默认消息(只有 data 字段,客户端用 onmessage 接收)
const sendMessage = (data) => {
res.write(`data: ${JSON.stringify(data)}\n\n`)
}
// 发送自定义事件(有 event 字段,客户端用 addEventListener 接收)
const sendCustomEvent = (eventName, data) => {
res.write(`event: ${eventName}\n`)
res.write(`data: ${JSON.stringify(data)}\n\n`)
}
// 定时推送不同类型的消息
const timer = setInterval(() => {
// 发送默认消息
sendMessage({ message: 'Hello', time: new Date() })
// 发送通知事件(客户端需要 addEventListener('notification') 接收)
sendCustomEvent('notification', { msg: '有新消息' })
// 发送更新事件(客户端需要 addEventListener('update') 接收)
sendCustomEvent('update', { progress: 50 })
}, 1000)
// 客户端断开时清理
req.on('close', () => {
clearInterval(timer)
})
})常见问题
SSE vs WebSocket
| 特性 | SSE | WebSocket |
|---|---|---|
| 通信方向 | 单向(服务器→客户端) | 双向 |
| 协议 | HTTP | WebSocket 协议 |
| 重连 | 自动重连 | 需手动实现 |
| 浏览器支持 | 广泛支持 | 广泛支持 |
| 复杂度 | 简单 | 相对复杂 |
| 适用场景 | 服务器推送、实时更新 | 聊天、游戏、协作编辑 |
选择建议:
- 只需要服务器推送 → 使用 SSE
- 需要双向实时通信 → 使用 WebSocket
