一、SSE是什么?—— “单相思”技术界的扛把子
SSE(Server-Sent Events),字面翻译是“服务器发送事件”,但你可以理解为——服务器单方面对客户端的“疯狂输出”。
- 特点:基于HTTP协议,服务器单向推送数据,客户端只能含泪接受(或者假装没收到)。
- 适用场景:
- 老板的实时工作通知(“这个需求今晚必须上线!”)
- 股市韭菜的实时行情播报(“您的股票已跌穿地心!”)
- 女朋友的微信消息轰炸(“你怎么不回我???”)
SSE vs WebSocket:
- SSE:像电台广播,你只能听,不能打电话互动(单向)。
- WebSocket:像微信语音,双方随时互喷(双向)。
总结:SSE是“舔狗模式”,WebSocket是“双向奔赴”。
二、Spring Boot集成SSE——3分钟速成
1. 依赖?不存在的!
Spring Boot早已内置SSE支持,只需一个spring-boot-starter-web,连“谢谢”都不用说。
2. 服务端代码:SseEmitter的“深情告白”
@GetMapping(path = "/sse-demo", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
public SseEmitter pushStockPrice() {
SseEmitter emitter = new SseEmitter(60_000L); // 设置60秒超时(超时后自动分手)
// 开个线程定时发送数据(避免主线程被拖垮)
Executors.newSingleThreadScheduledExecutor().scheduleAtFixedRate(() -> {
try {
String data = "股票价格:" + (Math.random() * 100);
emitter.send(data);
} catch (IOException e) {
emitter.completeWithError(e); // 发送失败?直接拉黑!
}
}, 0, 1, TimeUnit.SECONDS);
return emitter;
}
关键点:
produces = TEXT_EVENT_STREAM_VALUE:告诉浏览器这是SSE流。SseEmitter:Spring的“快递小哥”,负责把数据打包送给客户端。
3. 前端代码:EventSource的“痴心等待”
const eventSource = new EventSource('/sse-demo');
eventSource.onmessage = (e) => {
console.log("收到消息:" + e.data);
// 如果是股票价格,建议此处添加“摔键盘”逻辑
};
注意:
- 数据格式必须遵守SSE规范:
data: xxx\n\n(少了换行?服务器直接装死)。 - 默认自动重连(像极了吵架后主动求和的你)。
三、实战案例——从“摸鱼倒计时”到“老板监控系统”
案例1:摸鱼倒计时(终极打工人必备)
需求:实时显示离下班还有多久,精确到秒。
代码逻辑:
emitter.send("距离下班还有:" + calculateTimeRemaining() + "秒!");
效果:前端页面每秒刷新一次,打工人从此告别焦虑。
案例2:老板监控系统(慎用!)
需求:实时监控员工电脑屏幕(开玩笑的,别当真)。
伪代码:
// 每隔5秒截屏一次,Base64编码后推送
emitter.send(ScreenshotUtils.captureScreen());
风险:可能触发劳动仲裁。
案例3:在线求夸机器人(治愈系SSE)
需求:用户点击按钮,服务器推送一句随机彩虹屁。
代码:
String[] compliments = {"你的代码像诗一样!", "你的发际线很安全!", "你简直是团队的CTO!"};
emitter.send(compliments[new Random().nextInt(3)]);
效果:程序员幸福感提升200%。
四、SSE原理——HTTP的“长跑运动员”
底层机制:
- 长连接:客户端发起请求后,服务器不关闭连接,持续发送数据(
Content-Type: text/event-stream)。 - 数据格式:
event: 下班提醒 data: 还有30分钟! id: 666 retry: 5000event:自定义事件类型(比如“摸鱼事件”)。id:事件ID(用于断线重连时续播)。retry:断线后重试时间(单位:毫秒)。
Spring的魔法:
SseEmitter背后是异步Servlet机制,非阻塞处理请求。- 数据通过HTTP流(Chunked Transfer Encoding)分块传输。
**五、避坑指南
-
中文乱码:
- 症状:前端收到“淇℃伅”等乱码。
- 解药:设置响应编码为UTF-8(见代码):
emitter.send(SseEmitter.event().data("你好", MediaType.TEXT_PLAIN));
-
连接泄漏:
- 症状:客户端关闭页面,服务器还在疯狂发送数据。
- 解药:监听
onCompletion和onTimeout,及时回收Emitter:emitter.onCompletion(() -> log.info("连接已关闭!")); emitter.onTimeout(() -> log.info("连接超时!"));
-
跨域问题:
- 症状:前端报错“No 'Access-Control-Allow-Origin' header”。
- 解药:Spring Security中配置CORS,或添加
@CrossOrigin注解。
-
IE浏览器不支持:
- 症状:IE用户表示“这是什么鬼”。
- 解药:使用
EventSource的Polyfill库,或优雅降级为轮询。
六、最佳实践——SSE的“优雅姿势”
- 超时设置:
- 太长浪费资源,太短容易分手,建议30秒-5分钟。
- 数据压缩:
- 如果推送JSON大数据,开启GZIP压缩(节省带宽,老板省钱)。
- 心跳机制:
- 每隔一段时间发送空注释(
:\n\n),防止代理服务器掐断连接。
- 每隔一段时间发送空注释(
- 鉴权加持:
- 敏感数据?在URL中添加Token,或结合Spring Security拦截非法请求。
七、面试考点——SSE的灵魂八连问
-
SSE和WebSocket最大的区别是什么?
- 答:SSE是单向HTTP推送,WebSocket是双向TCP协议。
-
如何实现SSE的断线重连?
- 答:浏览器自动重试(根据
retry字段),服务端用id追踪事件位置。
- 答:浏览器自动重试(根据
-
SSE能上传文件吗?
- 答:不能!SSE是单向的,上传文件得用HTTP POST或WebSocket。
-
SseEmitter底层依赖什么技术?
- 答:Servlet 3.0+的异步处理机制,比如
DeferredResult。
- 答:Servlet 3.0+的异步处理机制,比如
-
SSE支持哪些浏览器?
- 答:除了IE,其他现代浏览器都支持(IE:你礼貌吗?)。
-
如何推送JSON数据?
- 答:
emitter.send(SseEmitter.event().data(myObject, MediaType.APPLICATION_JSON))。
- 答:
-
SSE和长轮询(Long Polling)的区别?
- 答:SSE是长连接,一次连接持续推送;长轮询是反复短连接。
-
高并发下SSE会有什么问题?
- 答:大量长连接占用线程和内存,需结合响应式编程(如WebFlux)优化。
八、总结——SSE的“人生哲学”
SSE是实时通信界的“经济适用男”:
- 优点:简单、省资源、兼容HTTP生态。
- 缺点:单向、IE不支持、不能撩回去。
适用场景口诀:
低频单向用SSE,高频互动WebSocket,
IE用户请绕道,老板需求别乱搞。
终极忠告:
- 爱TA就及时回应(处理超时),
- 不爱别拖泥带水(关闭连接)。