springboot项目建立sse接口
在Spring Boot项目中实现SSE(Server-Sent Events)接口非常简单,Spring框架提供了专门的SseEmitter
类来处理SSE连接。下面我将展示如何在Spring Boot中创建一个完整的SSE接口,包括服务端和客户端示例:
服务端实现(Spring Boot)
1. 添加依赖
如果是Maven项目,确保spring-boot-starter-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;import java.io.IOException;
import java.util.concurrent.CopyOnWriteArrayList;@RestController
public class SseController {// 存储所有客户端的SseEmitterprivate final CopyOnWriteArrayList<SseEmitter> emitters = new CopyOnWriteArrayList<>();// 客户端订阅SSE的接口@GetMapping(path = "/sse", produces = MediaType.TEXT_EVENT_STREAM_VALUE)public SseEmitter subscribe() {SseEmitter emitter = new SseEmitter(60_000L); // 设置超时时间60秒// 添加到emitters列表emitters.add(emitter);// 设置回调emitter.onCompletion(() -> emitters.remove(emitter));emitter.onTimeout(() -> emitters.remove(emitter));emitter.onError((e) -> emitters.remove(emitter));return emitter;}// 向所有客户端发送消息public void sendMessageToAll(String message) {emitters.forEach(emitter -> {try {// 发送数据emitter.send(SseEmitter.event().id(String.valueOf(System.currentTimeMillis())).name("message").data(message));} catch (IOException e) {// 发送失败时移除emitteremitter.completeWithError(e);emitters.remove(emitter);}});}
}
3. 创建定时任务发送消息(可选)
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;@Component
@EnableScheduling
public class MessageScheduler {private final SseController sseController;public MessageScheduler(SseController sseController) {this.sseController = sseController;}// 每5秒向所有客户端发送一次消息@Scheduled(fixedRate = 5000)public void sendPeriodicMessages() {String message = "Server time: " + System.currentTimeMillis();sseController.sendMessageToAll(message);}
}
客户端实现(JavaScript)
<!DOCTYPE html>
<html>
<head><meta charset="UTF-8"><title>SSE Demo</title>
</head>
<body><h1>SSE实时消息</h1><div id="messages"></div><script>// 创建EventSource连接const eventSource = new EventSource('/sse');// 监听默认消息事件eventSource.onmessage = function(event) {const messageElement = document.createElement('div');messageElement.textContent = `[${new Date().toLocaleTimeString()}] ${event.data}`;document.getElementById('messages').appendChild(messageElement);};// 监听自定义事件(如果服务端发送了name字段)eventSource.addEventListener('message', function(event) {console.log('Custom event received:', event.data);});// 错误处理eventSource.onerror = function(error) {console.error('EventSource failed:', error);if (eventSource.readyState === EventSource.CLOSED) {console.log('Connection closed by the server');}};</script>
</body>
</html>
关键技术点说明
-
SseEmitter类
SseEmitter
是Spring提供的处理SSE的核心类,用于管理服务器到客户端的长连接。- 支持设置超时时间、完成回调、错误处理等。
-
消息格式
- 服务端发送的消息默认格式为:
id: [唯一标识]\n event: [事件类型]\n data: [消息内容]\n\n
- 服务端发送的消息默认格式为:
-
心跳机制
- 长时间无数据时,需定期发送注释行(以
:
开头)保持连接:emitter.send(SseEmitter.event().comment("heartbeat"));
- 长时间无数据时,需定期发送注释行(以
-
错误处理
- 客户端可通过
eventSource.onerror
监听连接错误。 - 服务端需在发送失败时移除失效的
SseEmitter
。
- 客户端可通过
测试与验证
-
启动Spring Boot应用,访问
http://localhost:8080/sse
- 浏览器会保持长连接,控制台显示HTTP 200状态。
-
查看客户端接收消息
- 打开HTML页面,每5秒会显示一条新消息。
-
手动发送消息
- 可通过调用
SseController
的sendMessageToAll
方法测试消息推送。
- 可通过调用
生产环境优化建议
-
连接管理
- 使用
ConcurrentHashMap
存储不同用户的SseEmitter
,支持定向推送。 - 限制最大连接数,避免资源耗尽。
- 使用
-
异步处理
- 使用
@Async
注解让SSE处理在独立线程中执行,避免阻塞Servlet容器。
- 使用
-
安全加固
- 添加身份验证(如JWT)保护SSE接口。
- 配置CORS策略,限制访问来源。
-
高可用部署
- 使用Redis等中间件实现跨节点的消息广播。
- 部署多个服务实例,通过负载均衡器分发请求。
常见问题与解决方案
问题 | 原因分析 | 解决方案 |
---|---|---|
连接频繁断开 | Nginx等代理默认有连接超时限制 | 配置代理proxy_read_timeout |
消息丢失 | SseEmitter未正确管理 | 添加错误处理和重试机制 |
跨域问题 | 未配置CORS | 添加@CrossOrigin 注解 |
高并发性能问题 | 同步处理阻塞线程池 | 使用@Async 和异步线程池 |
通过以上步骤,你可以在Spring Boot项目中成功实现SSE接口,满足实时消息推送的需求。