前言
说真的,有些技术就像工具箱里那把不起眼的螺丝刀,不用到拧螺丝那天,你永远不知道它比电钻还好使。
之前总听人说 SSE(Server-Sent Events,服务器推送),但一直没啥机会上手,直到上个月接了个坐席系统:坐席和客户打电话时,系统得像ChatGPT那样,边说边把语音转成文字弹出来,还不能卡顿。
当时第一反应是"这简单啊,直接上WebSocket呗",结果被后端老哥怼了:"咱项目里已经有两个模块在用WebSocket了,现在再加一个?再开怕不是要被运维拉进黑名单!"
那就退而求其次,用轮询?结果一算账更崩溃:假设500ms轮询一次,一个坐席一天接200通电话,每通平均10分钟,光转文字接口就要被轮24万次!这服务器不得直接跪?
这时候,我突然想起 SSE 不就十分契合当前场景嘛,接下来我们就一起了解一下 SSE 吧。
SSE 介绍
**SSE(Server-Sent Events)**是一种基于HTTP协议的服务器推送技术,允许服务器主动向客户端(如浏览器)单向推送实时数据,适用于需要服务器单向实时通信的场景。
SSE 优点
1. 简单易用
- 协议简单:基于普通 HTTP/HTTPS 协议,无需特殊协议
- API 简洁 :浏览器端使用
EventSource
API,几行代码即可实现 - 自动重连:内置断线重连机制
2. 单向服务器推送
- 实时性好:服务器可以随时推送新数据
- 低延迟:相比轮询方式,减少了不必要的请求
3. 轻量级
- 开销小:相比 WebSocket,协议头更简单
- 兼容性好:基于 HTTP,更容易通过防火墙和代理
4. 功能完善
- 支持事件类型:可以发送不同类型的事件
- 内置消息ID:支持断线后恢复,避免消息丢失
- 自动UTF-8解码:处理文本数据更方便
SSE 缺点
1. 功能限制
- 仅支持文本:不能直接传输二进制数据
- 单向通信:只能服务器→客户端,不能客户端→服务器
- 无压缩:HTTP 头部不支持压缩
2. 浏览器限制
- 连接数限制:浏览器对同一域名下的并发 SSE 连接数有限制(通常6个)
- 旧版IE不支持:IE/Edge 旧版本不支持,需要 polyfill
3. 服务器实现
- 保持连接:服务器需要维护长连接,可能增加负担
- 超时处理:需要正确处理连接超时和断开
4. 其他限制
- Cookie限制:某些浏览器在SSE中不会发送Cookie
- 代理问题:某些代理服务器可能缓冲SSE流
- 请求头限制 :浏览器的原生支持EventSource不支持配置请求头
兼容性
图片来自 EventSource - Web API | MDN

浏览器 API - EventSource
实例属性
readyState
表明连接的当前状态,该属性只读,有三个属性
0
:connecting 连接还没有建立或者重连中1
:open 连接已经建立2
: close 连接已断开
withCredentials
一个布尔值,表示 EventSource
对象是否使用跨源资源共享凭据(cookie)来实例化(true
),或者不使用(false
,即默认值)
ps:关闭连接的话调用实例的
close
方法就行。
使用示例
js
if ('EventSource' in window) {
// 创建 EventSource 对象连接服务器,并携带 cookie
const eventSource = new EventSource('/sse-endpoint', { withCredentials: true });
// `0` --- connecting `1` --- open // `2` --- close
console.log(sse.readyState)
// 连接成功回调
eventSource.onopen = function (event) {
console.log('连接成功:');
};
// 监听消息事件
eventSource.onmessage = function(event) {
console.log('收到消息:', event.data);
// 消息结束通知
if(event.data.done) {
eventSource.close() // 关闭sse连接
}
};
// 监听自定义事件
eventSource.addEventListener('customEvent', function(event) {
console.log('自定义事件:', event.data);
});
// 错误处理
eventSource.onerror = function(error) {
console.error('SSE 错误:', error);
};
} else {
console.log('当前浏览器不支持 EventSource')
}
使用中遇到的问题
浏览器原生的实现不能更改请求头 ,解决办法:使用 fetch
模拟实现,或者使用第三方库:event-source-polyfill,@microsoft/fetch-event-source
服务端(node.js)示例
js
const http = require('http');
http.createServer((req, res) => {
if (req.url === '/sse-endpoint') {
res.writeHead(200, {
'Content-Type': 'text/event-stream',
'Cache-Control': 'no-cache',
'Connection': 'keep-alive'
});
// 发送初始消息
res.write('data: 连接已建立\n\n');
// 定时发送消息
let counter = 0;
const interval = setInterval(() => {
counter++;
res.write(`data: 这是消息 ${counter}\n\n`);
// 发送自定义事件
if (counter % 3 === 0) {
res.write(`event: customEvent\n`);
res.write(`data: 这是自定义事件消息 ${counter}\n\n`);
}
// 测试结束连接
if (counter === 10) {
res.write('event: close\n');
res.write('data: 连接即将关闭\n\n');
clearInterval(interval);
res.end();
}
}, 1000);
// 客户端断开连接时清理
req.on('close', () => {
clearInterval(interval);
res.end();
});
} else {
res.writeHead(404);
res.end();
}
}).listen(3000);
console.log('SSE 服务器运行在 http://localhost:3000');
总结
说到底,技术从来不是孤立的炫技场,而是业务场景的"最佳拍档"。就像给不同身材的人挑衣服,SSE这匹快马就该用在实时消息推送这类"长跑赛道"上,而AI对话、语音转写这些需要双向奔赴的场景,正是它大显身手的地方。选技术就像点菜,不追求满汉全席,能把业务这桌菜烧出滋味,就是真功夫。
博客主要记录一些学习的文章,如有不足,望大家指出,谢谢。