主动推送数据到前端的SSE技术原理以及如何使用
前言
首先,SSE全称是Server-Sent Events,是一种服务器向客户端单向推送数据的技术。它基于HTTP协议,使用长连接,允许服务器主动发送消息到浏览器。相比WebSocket,SSE更简单,但只支持单向通信,适合服务器向客户端推送实时数据的场景,比如实时通知、股票行情等。
Spring MVC提供了对SSE的支持,主要是通过SseEmitter类。需要创建一个Controller,返回SseEmitter对象,然后通过这个对象发送数据。可能需要维护一个SseEmitter的列表,以便在需要的时候向所有客户端广播消息。
然后前端部分,Vue作为前端框架,需要用EventSource API来接收SSE的事件。在Vue组件中,可以在mounted生命周期钩子中创建EventSource实例,监听服务器的事件流,处理接收到的数据,并在组件销毁时关闭连接,防止内存泄漏。
SSE 技术原理
Server-Sent Events (SSE) 是一种基于 HTTP 的单向通信协议,允许服务器主动向客户端推送数据。其核心特点:
- 单向通信:仅服务端 → 客户端。
- 长连接:通过
text/event-stream
保持连接。 - 自动重连:客户端自动尝试重新连接。
- 轻量级:相比 WebSocket,无需复杂握手协议。
Spring Boot 后端实现
1. 添加依赖
<!-- Spring Web -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
2. 创建 SSE 控制器
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.servlet.mvc.method.annotation.SseEmitter;
@RestController
@CrossOrigin(origins = "*") // 允许跨域
public class SseController {
private final Map<String, SseEmitter> emitters = new ConcurrentHashMap<>();
// 客户端订阅
@GetMapping(path = "/sse/subscribe", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
public SseEmitter subscribe() {
SseEmitter emitter = new SseEmitter(60_000L); // 超时时间 60秒
String clientId = UUID.randomUUID().toString();
emitters.put(clientId, emitter);
// 监听连接关闭
emitter.onCompletion(() -> emitters.remove(clientId));
emitter.onTimeout(() -> emitters.remove(clientId));
return emitter;
}
// 向所有客户端广播消息
public void broadcast(String message) {
emitters.forEach((id, emitter) -> {
try {
emitter.send(SseEmitter.event()
.data(message) // 发送数据
.id(id) // 事件ID(可选)
.name("sse-event") // 事件名(可选)
);
} catch (IOException e) {
emitter.completeWithError(e); // 处理异常
}
});
}
}
3. 触发消息推送
在业务逻辑中调用 broadcast()
方法:
@Service
public class NotificationService {
@Autowired
private SseController sseController;
public void sendRealTimeData(String data) {
sseController.broadcast(data);
}
}
Vue 前端实现
1. 连接 SSE
在 Vue 组件中建立 SSE 连接:
export default {
data() {
return {
eventSource: null,
messages: [],
};
},
mounted() {
this.connectSSE();
},
beforeDestroy() {
if (this.eventSource) {
this.eventSource.close(); // 组件销毁时关闭连接
}
},
methods: {
connectSSE() {
this.eventSource = new EventSource('http://localhost:8080/sse/subscribe');
// 监听消息事件
this.eventSource.addEventListener('sse-event', (event) => {
this.messages.push(JSON.parse(event.data));
});
// 处理错误
this.eventSource.onerror = (error) => {
console.error('SSE Error:', error);
this.reconnect(); // 尝试重连
};
},
reconnect() {
if (this.eventSource) {
this.eventSource.close();
setTimeout(() => this.connectSSE(), 3000); // 3秒后重连
}
},
},
};
2. 显示数据
在模板中渲染接收到的消息:
<template>
<div>
<h3>实时消息:</h3>
<ul>
<li v-for="(msg, index) in messages" :key="index">{{ msg }}</li>
</ul>
</div>
</template>
关键配置与优化
1. 跨域处理
- 后端:使用
@CrossOrigin
或全局 CORS 配置。 - 前端:开发环境下可通过 Vue 代理解决:
// vue.config.js module.exports = { devServer: { proxy: { '/sse': { target: 'http://localhost:8080', ws: false, // SSE 不需要 WebSocket changeOrigin: true, }, }, }, };
2. 消息格式
- 标准 SSE 格式:每条消息以
data:
开头,以\n\n
结束。emitter.send("data: " + message + "\n\n");
- JSON 支持:发送结构化数据:
emitter.send(SseEmitter.event().data(JSON.stringify({ user: "Alice", value: 42 })));
3. 性能优化
- 心跳机制:定期发送空消息保持连接活跃。
- 背压控制:避免高频消息导致客户端阻塞。
- 连接池管理:限制最大并发连接数。
完整流程示例
- 客户端订阅:浏览器访问
http://localhost:8080/sse/subscribe
。 - 服务端存储连接:将
SseEmitter
存入 Map。 - 业务触发推送:调用
broadcast("New data")
。 - 前端接收更新:Vue 组件实时渲染消息列表。
对比 WebSocket
特性 | SSE | WebSocket |
---|---|---|
通信方向 | 单向(服务端 → 客户端) | 双向 |
协议 | HTTP | 自定义协议 (ws/wss) |
自动重连 | ✅ | 需手动实现 |
复杂度 | 低 | 高 |
适用场景 | 实时通知、日志流 | 聊天室、游戏 |
通过 SSE 技术,可轻松实现后端到前端的实时数据推送,结合 Spring Boot 和 Vue 的生态,为前后端分离项目提供高效、简洁的实时通信方案。