使用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服务端推送的资料请关注脚本之家其它相关文章!
相关文章
详解Spring Boot实战之Restful API的构建
这篇文章主要介绍了详解Spring Boot实战之Restful API的构建,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧2018-01-01
基于springboot 配置文件context-path的坑
这篇文章主要介绍了基于springboot 配置文件context-path的坑,基于很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教2022-01-01
Java中Json与List、Map、entity的互相转化
在开发中,Json转换的场景往往也就是那么几个,本文主要介绍了Java中Json与List、Map、entity的互相转化,具有一定的参考价值,感兴趣的可以了解一下2022-07-07
SpringBoot使用Maven打包后运行失败的问题解决详解
在使用 Spring Boot 开发项目时,我们通常会使用 Maven 作为构建工具,但有时会出现运行失败的情况,本文将从多个角度分析常见错误场景,并提供详细的排查和解决方案,希望对大家有所帮助2025-07-07


最新评论