SSE详解
一、什么是 SSE?
SSE(Server-Sent Events)是一种基于 HTTP 协议的服务器推送技术,允许服务器主动向客户端(如浏览器)推送实时数据。与传统的轮询(Polling)或长轮询(Long Polling)不同,SSE 通过单一的持久连接实现数据的实时传输,客户端无需频繁发起请求。
二、SSE 的核心特点
-
单向通信
SSE 是单向的,服务器可以主动推送数据到客户端,但客户端无法直接通过 SSE 向服务器发送数据(如果需要双向通信,需结合其他技术如 WebSocket)。 -
基于 HTTP 协议
SSE 使用标准的 HTTP 协议,无需额外的协议或端口配置,兼容性好,易于实现。 -
轻量级
相比 WebSocket,SSE 的实现更简单,代码量更少,适合简单的实时数据推送场景。 -
自动重连
如果连接断开,浏览器会自动尝试重新连接,开发者无需手动处理重连逻辑。 -
支持事件类型
服务器可以发送不同类型的事件,客户端可以根据事件类型执行不同的操作。 -
支持消息 ID
每条消息可以包含一个唯一的 ID,用于断线重连后恢复消息流。
三、SSE 的工作原理
-
客户端发起请求
客户端通过 JavaScript 创建一个EventSource
对象,并指定服务器的 URL。const eventSource = new EventSource('/sse-endpoint');
-
服务器响应
服务器返回的响应头需要包含以下内容:Content-Type: text/event-stream Cache-Control: no-cache Connection: keep-alive
响应体由一系列事件组成,每个事件以
\n\n
分隔。 -
数据格式
每个事件可以包含以下字段:data
: 消息内容(必须)event
: 事件类型(可选)id
: 消息 ID(可选)retry
: 重连时间(可选,单位:毫秒)
示例:
data: Hello, world! event: message id: 123 retry: 10000data: Another message
-
客户端接收数据
客户端可以通过以下方式监听事件:eventSource.onmessage = (event) => {console.log('Received:', event.data); };eventSource.addEventListener('message', (event) => {console.log('Custom event:', event.data); });
四、SSE 的应用场景
-
实时通知
如社交媒体的实时消息提醒、邮件通知等。 -
实时数据更新
如股票行情、天气预报、体育比分等。 -
日志监控
实时推送服务器日志或应用状态。 -
进度更新
如文件上传进度、任务执行进度等。
五、SSE 与 WebSocket 的对比
特性 | SSE | WebSocket |
---|---|---|
通信方向 | 单向(服务器到客户端) | 双向(全双工) |
协议 | 基于 HTTP | 独立协议(ws:// 或 wss://) |
实现复杂度 | 简单 | 较复杂 |
数据格式 | 文本(UTF-8) | 支持文本和二进制 |
自动重连 | 支持 | 需手动实现 |
适用场景 | 简单的实时数据推送 | 复杂的双向实时通信(如聊天应用) |
六、SSE 的优缺点
优点:
- 实现简单,代码量少。
- 基于 HTTP,兼容性好。
- 自动重连机制,可靠性高。
- 适合单向实时数据推送场景。
缺点:
- 仅支持单向通信,无法满足双向实时通信需求。
- 只能传输文本数据,二进制数据需编码后传输。
- 浏览器兼容性有限(IE 不支持)。
七、SSE 的实现示例
服务器端(Node.js)
const express = require('express');
const app = express();app.get('/sse-endpoint', (req, res) => {res.setHeader('Content-Type', 'text/event-stream');res.setHeader('Cache-Control', 'no-cache');res.setHeader('Connection', 'keep-alive');let counter = 0;const interval = setInterval(() => {counter++;res.write(`data: ${counter}\n\n`);}, 1000);req.on('close', () => {clearInterval(interval);res.end();});
});app.listen(3000, () => {console.log('Server running on port 3000');
});
客户端
const eventSource = new EventSource('http://localhost:3000/sse-endpoint');eventSource.onmessage = (event) => {console.log('Received:', event.data);
};eventSource.onerror = (error) => {console.error('Error:', error);
};
八、SSE与Springboot应用实例
在Spring Boot中应用SSE(Server-Sent Events)可实现高效的实时数据推送,适用于股票行情、通知推送等单向通信场景。以下是具体实践方案及关键点:
1. 基础实现步骤
- 添加依赖:确保项目中包含
spring-boot-starter-web
依赖。 - 创建控制器:使用
SseEmitter
处理客户端连接,通过@GetMapping
注解定义SSE端点,设置produces = MediaType.TEXT_EVENT_STREAM_VALUE
。 - 消息推送:通过
SseEmitter.send()
方法发送数据,支持文本或JSON格式。 - 客户端监听:前端使用
EventSource
对象连接SSE端点,通过onmessage
或addEventListener
监听事件。
2. 核心代码示例
服务端代码
@RestController
@RequestMapping("/sse")
public class SseController {private final Map<String, SseEmitter> emitters = new ConcurrentHashMap<>();@GetMapping("/connect")public SseEmitter connect(@RequestParam String userId) {SseEmitter emitter = new SseEmitter(60_000L);emitters.put(userId, emitter);emitter.onCompletion(() -> emitters.remove(userId));emitter.onTimeout(() -> emitters.remove(userId));// 模拟异步发送数据new Thread(() -> {try {for (int i = 0; i < 10; i++) {emitter.send(SseEmitter.event().data("Message " + i).id(String.valueOf(i)).name("sse-event"));Thread.sleep(1000);}emitter.complete();} catch (Exception e) {emitter.completeWithError(e);}}).start();return emitter;}// 广播消息示例public void broadcast(String message) {emitters.forEach((id, emitter) -> {try {emitter.send(message);} catch (IOException e) {emitters.remove(id);}});}
}
前端代码
<script>const eventSource = new EventSource('/sse/connect?userId=123');eventSource.onmessage = (event) => {console.log('Received:', event.data);};eventSource.addEventListener('sse-event', (event) => {console.log('Custom event:', event.data);});eventSource.onerror = () => {console.error('Connection error');eventSource.close();};
</script>
3. 关键优化点
- 连接管理:使用
ConcurrentHashMap
存储连接,及时移除无效连接,避免内存泄漏。 - 超时设置:合理设置
SseEmitter
的超时时间(如60秒),避免长时间占用资源。 - 心跳机制:定期发送空消息(如
comment("heartbeat")
)保持连接活跃,防止超时断开。 - 异步处理:通过线程池或
@Async
异步发送消息,避免阻塞主线程。 - 跨域配置:在
WebMvcConfigurer
中配置CORS,允许前端跨域访问。
4. 进阶场景
- 消息分组:通过URL参数(如
groupId
)区分客户端组,实现定向推送。 - 消息广播:结合
SimpMessagingTemplate
或Redisson
实现多客户端广播。 - 结构化数据:发送JSON数据时,设置
MediaType.APPLICATION_JSON
并解析前端数据。
5. 注意事项
- 单向通信:SSE仅支持服务器到客户端的单向推送,如需双向通信,需结合WebSocket。
- 浏览器兼容性:IE不支持SSE,需提供降级方案(如轮询)。
- 性能调优:调整Tomcat线程池配置,避免高并发下资源耗尽。
6. 对比WebSocket
特性 | SSE | WebSocket |
---|---|---|
通信方向 | 单向(服务器→客户端) | 双向(全双工) |
协议 | 基于HTTP | 独立协议(ws://或wss://) |
实现复杂度 | 简单 | 较复杂 |
适用场景 | 实时通知、数据更新 | 聊天、游戏等双向实时通信 |
SSE在Spring Boot中适合实现简单的实时数据推送,具有实现简单、兼容性好等优点。通过合理管理连接、设置超时和心跳机制,可确保系统的稳定性和可靠性。对于需要双向通信的场景,建议结合WebSocket使用。
八、总结
SSE 是一种简单高效的服务器推送技术,适合单向实时数据推送场景。它的实现简单、兼容性好,但在双向通信和二进制数据传输方面存在局限性。在实际应用中,开发者应根据具体需求选择 SSE 或 WebSocket。