使用Spring SseEmitter实现服务端推送的示例代码
作者:gs80140
SseEmitter 是 Spring MVC 4.2+ 引入的一个类,专门用于实现 Server-Sent Events (SSE),相比于 WebSocket,SSE 更轻量(基于 HTTP 协议),且支持自动重连,所以本文给大家介绍了如何使用Spring SseEmitter实现服务端推送,需要的朋友可以参考下
SseEmitter 是 Spring MVC 4.2+ 引入的一个类,专门用于实现 Server-Sent Events (SSE)。简单来说,SSE 允许服务器在建立连接后,主动向客户端推送数据,而不需要客户端反复轮询。
相比于 WebSocket,SSE 更轻量(基于 HTTP 协议),且支持自动重连。它非常适合像实时通知、股价更新、大模型(LLM)流式输出这类单向推送场景。
核心原理解析
在传统的 HTTP 请求中,客户端发送请求,服务器返回响应,然后连接关闭。而 SseEmitter 改变了这种模式:
- 保持连接:服务器返回的响应头包含
Content-Type: text/event-stream。 - 分块传输:连接保持打开状态,服务器可以多次调用
emitter.send()发送数据包。 - 结束生命周期:手动调用
complete()或因为超时/错误触发onTimeout/onError。
前言
在实时性需求日益增长的今天,我们不一定非要动用“重型武器” WebSocket。如果你只需要服务器向客户端推送消息(如:进度条更新、ChatCompletion 响应),Spring 提供的 SseEmitter 可能是你的最佳选择。
一、 核心代码实现
使用 SseEmitter 通常分为三步:创建连接、保存引用、推送消息。
1. 控制器层 (Controller)
这是建立连接的入口。
@RestController
@RequestMapping("/api/sse")
public class SseController {
// 用于存储已连接的客户端,实际生产中建议使用专门的服务类管理
private static final Map<String, SseEmitter> emitters = new ConcurrentHashMap<>();
@GetMapping(value = "/subscribe/{userId}", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
public SseEmitter subscribe(@PathVariable String userId) {
// 设置超时时间(单位毫秒),0 表示永不超时
SseEmitter emitter = new SseEmitter(60_000L);
emitters.put(userId, emitter);
// 注册回调
emitter.onCompletion(() -> emitters.remove(userId));
emitter.onTimeout(() -> emitters.remove(userId));
emitter.onError((e) -> emitters.remove(userId));
// 建立连接后立即发送一条消息,防止某些代理或浏览器认为连接已失效
try {
emitter.send(SseEmitter.event().name("INIT").data("Connected!"));
} catch (IOException e) {
emitter.completeWithError(e);
}
return emitter;
}
}
2. 消息推送逻辑
你可以从任何异步线程调用 send 方法。
public void sendMessage(String userId, String content) {
SseEmitter emitter = emitters.get(userId);
if (emitter != null) {
try {
// 可以发送纯文本,也可以发送封装好的对象(会自动转 JSON)
emitter.send(SseEmitter.event()
.id(UUID.randomUUID().toString())
.name("message") // 对应前端的事件名
.data(content)
.reconnectTime(3000)); // 提示客户端重连间隔
} catch (IOException e) {
emitters.remove(userId);
emitter.completeWithError(e);
}
}
}
二、 前端如何接收?
浏览器内置了 EventSource API,调用非常简单:
const eventSource = new EventSource('/api/sse/subscribe/user123');
eventSource.onmessage = (event) => {
console.log("收到通用消息:", event.data);
};
// 监听特定类型的事件 (对应后端的 .name("message"))
eventSource.addEventListener('message', (event) => {
const data = JSON.parse(event.data);
console.log("处理后的数据:", data);
});
eventSource.onerror = (error) => {
console.error("SSE 错误:", error);
eventSource.close();
};
三、 避坑指南(关键技巧)
- Nginx 配置:如果你使用了 Nginx 代理,必须配置
proxy_buffering off;和proxy_set_header Connection "";,否则 Nginx 会缓存消息直到缓冲区满才一次性发给前端。 - 容器线程限制:虽然
SseEmitter是异步的,但它依然占用一个 HTTP 连接。在高并发场景下,需要调整 Web 容器(如 Tomcat)的最大连接数。 - 超时处理:浏览器默认会在 SSE 断开后自动重连。在
onTimeout中,务必调用emitter.complete()来清理服务器资源。 - 跨域问题:确保 CORS 配置允许
Last-Event-ID等特殊请求头。
总结
SseEmitter 是 Spring 为我们封装的一把“轻巧的瑞士军刀”。它规避了 WebSocket 复杂的握手和协议转换,在处理流式输出(如 AI 对话响应)时表现极佳。
以上就是使用Spring SseEmitter实现服务端推送的示例代码的详细内容,更多关于Spring SseEmitter服务端推送的资料请关注脚本之家其它相关文章!
