springboot 实现sse
时间: 2025-04-10 16:36:30 浏览: 69
<think>好的,用户现在想了解如何在Spring Boot中实现SSE(Server-Sent Events)。之前他们已经询问过WebSocket和SSE的区别,现在可能正在实际开发中需要选择适合的服务端推送技术,而SSE更符合他们的需求。
首先,我需要回顾一下SSE的基本概念和适用场景。SSE适用于服务端向客户端单向推送数据的场景,比如实时通知、股票行情等。Spring Boot对SSE有良好的支持,通常通过SseEmitter来实现。
接下来,我需要考虑用户可能的背景。他们可能已经熟悉Spring Boot的基础,但需要具体的实现步骤和示例代码。因此,回答应该结构清晰,分步骤讲解,包括依赖配置、服务端实现、客户端代码以及测试方法。
要确保覆盖关键点:如何创建SSE端点,处理客户端连接,发送消息,以及客户端的接收方式。同时,需要注意异常处理和资源释放,避免内存泄漏。可能还需要提到一些高级配置,比如超时设置、事件重试等。
用户可能对异步处理有疑问,需要解释Spring中异步请求的处理方式,比如@Async注解和线程池配置。此外,实际应用中可能遇到的问题,比如客户端断开后的处理、如何保持连接等,也应该包含在注意事项中。
最后,提供一个完整的示例,包括服务端和客户端的代码,以及如何测试,这样用户可以直接参考并应用到自己的项目中。需要确保代码示例正确,符合Spring Boot的最新实践,同时避免不必要的复杂度,让用户容易理解和实现。</think>---
### **Spring Boot 实现 SSE(Server-Sent Events)分步指南**
SSE 是一种基于 HTTP 的服务端单向推送技术,适用于实时通知、日志流传输等场景。Spring Boot 通过 `SseEmitter` 类原生支持 SSE,以下是实现细节:
---
#### **一、核心依赖**
无需额外依赖,Spring Boot Web 已内置支持:
```xml
<!-- pom.xml -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
```
---
#### **二、服务端实现**
##### **1. 创建 SSE 控制器**
```java
@RestController
@RequestMapping("/sse")
public class SseController {
// 存储所有连接的客户端
private static final Map<String, SseEmitter> emitters = new ConcurrentHashMap<>();
// 客户端连接入口
@GetMapping("/connect")
public SseEmitter connect(@RequestParam String clientId) {
SseEmitter emitter = new SseEmitter(60_000L); // 超时时间60秒
// 注册客户端
emitters.put(clientId, emitter);
// 连接关闭或超时时的清理逻辑
emitter.onCompletion(() -> emitters.remove(clientId));
emitter.onTimeout(() -> emitters.remove(clientId));
return emitter;
}
// 向指定客户端推送消息
public static void sendMessage(String clientId, Object data) {
SseEmitter emitter = emitters.get(clientId);
if (emitter != null) {
try {
emitter.send(SseEmitter.event()
.data(data) // 推送数据
.reconnectTime(3000) // 断线重试时间(毫秒)
.id("EVENT_ID") // 事件ID(可选)
.name("EVENT_NAME")// 事件名称(可选)
);
} catch (IOException e) {
emitter.completeWithError(e); // 异常处理
emitters.remove(clientId);
}
}
}
}
```
##### **2. 消息推送服务**
```java
@Service
public class SseService {
// 模拟定时推送(实际场景可能来自MQ、数据库变更等)
@Scheduled(fixedRate = 1000)
public void pushData() {
String message = "Server Time: " + new Date();
SseController.sendMessage("client123", message);
}
}
```
---
#### **三、客户端实现**
##### **1. 浏览器端(JavaScript)**
```javascript
const eventSource = new EventSource('http://localhost:8080/sse/connect?clientId=client123');
// 监听默认消息(未指定事件名)
eventSource.onmessage = (event) => {
console.log('Received:', event.data);
};
// 监听自定义事件
eventSource.addEventListener('EVENT_NAME', (event) => {
console.log('Custom Event:', event.data);
});
// 错误处理
eventSource.onerror = (error) => {
console.error('SSE Error:', error);
eventSource.close();
};
```
##### **2. Java 客户端(使用 Spring RestTemplate)**
```java
public void listenSSE() {
RestTemplate restTemplate = new RestTemplate();
restTemplate.execute(
"http://localhost:8080/sse/connect?clientId=client123",
HttpMethod.GET,
null,
response -> {
try (BufferedReader reader = new BufferedReader(
new InputStreamReader(response.getBody(), StandardCharsets.UTF_8))) {
String line;
while ((line = reader.readLine()) != null) {
System.out.println("Received: " + line);
}
}
return null;
}
);
}
```
---
#### **四、关键配置**
##### **1. 异步支持配置**
```java
@Configuration
@EnableAsync
public class AsyncConfig implements AsyncConfigurer {
@Override
public Executor getAsyncExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(10);
executor.setMaxPoolSize(50);
executor.setQueueCapacity(100);
executor.setThreadNamePrefix("SSE-Async-");
executor.initialize();
return executor;
}
}
```
##### **2. 跨域配置(可选)**
```java
@Configuration
public class CorsConfig implements WebMvcConfigurer {
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/sse/**")
.allowedOrigins("http://localhost:3000")
.allowedMethods("GET");
}
}
```
---
#### **五、测试验证**
1. **浏览器测试**
直接访问 `http://localhost:8080/sse/connect?clientId=client123`,观察控制台输出
2. **命令行测试(curl)**
```bash
curl -v http://localhost:8080/sse/connect?clientId=client123
```
3. **服务端主动推送测试**
调用 `SseService.pushData()` 方法触发消息发送
---
#### **六、常见问题处理**
| 问题现象 | 解决方案 |
|--------------------------|--------------------------------------------------------------------------|
| 客户端接收延迟 | 检查服务器是否开启压缩,禁用响应压缩:`server.compression.enabled=false` |
| 连接自动断开 | 调整 `SseEmitter` 超时时间,增加心跳检测 |
| 高并发时内存泄漏 | 确保在 `onCompletion`/`onTimeout` 中清理 `emitters` Map |
| 浏览器兼容性问题 | 使用 `EventSource` polyfill(如 `eventsource` 库) |
---
#### **七、性能优化建议**
1. **连接管理**
使用 `ConcurrentHashMap` 存储 `SseEmitter`,避免线程阻塞
2. **心跳机制**
定期发送空消息保持连接:
```java
@Scheduled(fixedRate = 5000)
public void sendHeartbeat() {
emitters.forEach((id, emitter) -> {
try {
emitter.send(SseEmitter.event().comment(""));
} catch (IOException ignored) {}
});
}
```
3. **批量消息处理**
使用 `SseEmitter.event().data()` 支持多次发送,避免大报文传输
---
通过以上实现,您可以在 Spring Boot 中快速构建 SSE 服务端推送能力,结合具体的业务场景进行扩展优化。
阅读全文
相关推荐
















