Spring SseEmitter 系统详细讲解
Spring SseEmitter
系统详细讲解
一、SSE 基本概念(Server-Sent Events)
- SSE 是 HTML5 提出的标准。
- 客户端通过
EventSource
向服务端发起 一个长连接,服务器通过该连接持续向客户端发送事件。 - 相比 WebSocket(双向通信),SSE 是单向的:服务端 → 客户端。
- 本质上是基于 HTTP 协议的长连接。
示例(前端代码)
const eventSource = new EventSource("/sse/subscribe");eventSource.onmessage = function(event) {console.log("收到消息:", event.data);
};eventSource.onerror = function(e) {console.error("连接错误", e);
};
二、Spring 的 SseEmitter
作用
Spring 提供了 SseEmitter
类来简化 SSE 的开发,它本质上是一个 Controller 的返回对象,用于不断地往前端推送数据。
三、基本用法
1. 添加依赖(Spring Boot Web 项目一般已包含)
<!-- Maven 中无需额外添加,spring-boot-starter-web 已包含 -->
2. 控制器定义 SSE 接口
@RestController
public class SseController {@GetMapping("/sse/subscribe")public SseEmitter subscribe() {SseEmitter emitter = new SseEmitter(0L); // 不超时,或设置时间,如30_000LExecutors.newSingleThreadExecutor().submit(() -> {try {for (int i = 1; i <= 5; i++) {emitter.send("第 " + i + " 条消息");Thread.sleep(1000);}emitter.complete(); // 关闭连接} catch (Exception e) {emitter.completeWithError(e);}});return emitter;}
}
四、SseEmitter 的重要方法
方法 | 说明 |
---|---|
send(Object data) | 向客户端发送消息,支持字符串、JSON 等 |
send(Object data, MediaType mediaType) | 指定 MIME 类型发送 |
complete() | 正常关闭连接 |
completeWithError(Throwable ex) | 异常关闭连接 |
onCompletion(Runnable callback) | 设置连接关闭的回调 |
onTimeout(Runnable callback) | 设置超时回调 |
onError(Consumer<Throwable> callback) | 设置错误回调 |
setTimeout(long timeout) | 设置连接超时时间(默认 30 秒) |
五、注意事项
1. Content-Type
Content-Type: text/event-stream
Spring 会自动设置,只需:
@GetMapping(value = "/sse/subscribe", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
2. 浏览器自动重连机制
- 如果服务端关闭连接(未调用
.complete()
),浏览器会默认自动尝试重新连接。 - 禁止客户端重连可发送
retry: 0
。
3. 多用户支持
private final Map<String, SseEmitter> userEmitters = new ConcurrentHashMap<>();@GetMapping("/subscribe/{userId}")
public SseEmitter subscribe(@PathVariable String userId) {SseEmitter emitter = new SseEmitter(60_000L);userEmitters.put(userId, emitter);emitter.onCompletion(() -> userEmitters.remove(userId));emitter.onTimeout(() -> userEmitters.remove(userId));return emitter;
}public void sendToUser(String userId, String message) throws IOException {SseEmitter emitter = userEmitters.get(userId);if (emitter != null) {emitter.send(message);}
}
六、典型应用场景
- 实时进度条
- 实时通知系统
- 日志推送
- 后台任务结果反馈
七、SseEmitter 与 WebSocket 的对比
比较项 | SSE(SseEmitter) | WebSocket |
---|---|---|
通信方向 | 单向(服务端 → 客户端) | 双向 |
协议 | HTTP | ws:// 或 wss:// |
浏览器支持 | 广泛支持 | 广泛支持 |
实现复杂度 | 简单 | 需要管理连接 |
使用场景 | 实时推送 | 聊天、游戏等 |
八、SseEmitter 高级技巧
✅ 自定义事件名称
emitter.send(SseEmitter.event().name("customEvent").data("这是自定义事件")
);
前端监听:
eventSource.addEventListener("customEvent", function(event) {console.log("收到自定义事件:", event.data);
});
✅ 定时心跳保持连接
ScheduledExecutorService heartbeatScheduler = Executors.newScheduledThreadPool(1);
heartbeatScheduler.scheduleAtFixedRate(() -> {try {emitter.send("heartbeat");} catch (IOException e) {emitter.completeWithError(e);}
}, 0, 15, TimeUnit.SECONDS);
九、总结
特性 | 支持情况 |
---|---|
异步发送 | ✅ |
多用户并发 | ✅ |
超时与关闭管理 | ✅ |
自定义事件类型 | ✅ |
应用场景 | 实时推送、轻量通知 |