1、简述
SSE(Server-Sent Events)是一种基于HTTP协议的单向通信机制,允许服务器向浏览器持续发送实时更新。与WebSocket不同,SSE更简单,使用HTTP/1.1协议即可,不需要额外的协议升级。
SSE的特点:
-
单向通信:服务器推送数据给客户端,客户端无法向服务器发送消息。
-
简单易用:基于HTTP协议,无需复杂的配置。
-
浏览器支持:现代浏览器大多内置支持(如Chrome、Edge、Firefox等)。
2、Spring Boot 中的SSE实现
2.1 添加依赖
SSE无需额外的依赖,Spring Boot自带对SSE的支持。创建一个Spring Boot项目即可。
java
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
2.2 实现后端接口
使用MediaType.TEXT_EVENT_STREAM_VALUE
作为返回类型即可开启SSE。以下代码是一个简单的实现。
java
package com.example.sse.controller;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import java.time.LocalTime;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import java.util.stream.Stream;
@RestController
public class SseController {
@GetMapping(value = "/sse/stream", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
public Stream<String> stream() {
// 模拟数据流
return Stream.generate(() -> "当前时间:" + LocalTime.now())
.limit(10); // 限制10条消息
}
}
2.3 配置超时时间(可选)
默认情况下,Spring Boot的响应会超时。可以在application.properties
中调整超时时间:
java
server.servlet.session.timeout=30s
spring.mvc.async.request-timeout=30000
2.4 前端实现
SSE在前端通过EventSource
对象实现。以下是一个简单的前端示例:
html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>SSE Example</title>
</head>
<body>
<h1>实时消息</h1>
<div id="messages"></div>
<script>
const eventSource = new EventSource('/sse/stream');
eventSource.onmessage = function(event) {
const messagesDiv = document.getElementById('messages');
const newMessage = document.createElement('p');
newMessage.textContent = event.data;
messagesDiv.appendChild(newMessage);
};
eventSource.onerror = function() {
console.error('SSE连接出错,正在尝试重连...');
eventSource.close();
};
</script>
</body>
</html>
3、高级实践
使用Spring Scheduler推送数据,在实际场景中,可能需要定时向客户端推送数据。例如,监控系统定时更新。
java
package com.example.sse.service;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Service;
import org.springframework.web.servlet.mvc.method.annotation.SseEmitter;
import java.io.IOException;
import java.util.concurrent.CopyOnWriteArrayList;
@Service
public class SsePushService {
private final CopyOnWriteArrayList<SseEmitter> emitters = new CopyOnWriteArrayList<>();
public SseEmitter subscribe() {
SseEmitter emitter = new SseEmitter(30_000L);
emitters.add(emitter);
emitter.onCompletion(() -> emitters.remove(emitter));
emitter.onTimeout(() -> emitters.remove(emitter));
return emitter;
}
public void pushMessage(String message) {
for (SseEmitter emitter : emitters) {
try {
emitter.send(message, MediaType.TEXT_PLAIN);
} catch (IOException e) {
emitters.remove(emitter);
}
}
}
}
创建一个控制器订阅和推送消息:
java
package com.example.sse.controller;
import com.example.sse.service.SsePushService;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class SsePushController {
private final SsePushService ssePushService;
public SsePushController(SsePushService ssePushService) {
this.ssePushService = ssePushService;
}
@GetMapping("/sse/subscribe")
public SseEmitter subscribe() {
return ssePushService.subscribe();
}
@GetMapping("/sse/push")
public void pushMessage() {
ssePushService.pushMessage("当前时间:" + System.currentTimeMillis());
}
}
注意事项:
-
浏览器兼容性:SSE不支持IE,但现代浏览器支持良好。
-
连接断开处理:可通过
EventSource
的onerror
事件重新连接。 -
性能问题:对大量订阅者时,需考虑使用分布式消息队列优化(如Kafka)。
-
超时时间:默认30秒超时,需要根据实际需求调整。
4、适用场景
-
实时通知:如监控系统的告警推送。
-
实时更新:如股票行情、体育比分。
-
消息流:如系统日志、任务进度。