随着Web应用的不断发展,实时数据传输的需求变得越来越普遍。传统的轮询方法不仅效率低下,而且在高并发情况下会对服务器造成不必要的压力。为了解决这个问题,Server-Sent Events (SSE) 应运而生,它允许服务器端主动向客户端推送更新。
Server-Sent Events
Server-Sent Events 是一种允许服务器向浏览器发送实时更新的技术。不同于WebSocket的全双工通信方式,SSE更专注于单向的数据流,即从服务器到客户端的数据推送。
这种方式对于需要实时更新的场景非常有用,当前主流的大模型平台,比如ChatGPT、通义千问、文心一言,对话时采用的就是SSE。
SSE 本质是一个基于 http 协议的通信技术。
SSE的应用
引入依赖
spring-boot-starter-web 中默认已经引用了 sse,所以我们不需要额外引入其他依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
基本用法
@RestController
public class SseController {
@GetMapping("/sse")
public SseEmitter handleSse() {
SseEmitter emitter = new SseEmitter();
// 异步处理发送事件
new Thread(() -> {
try {
// 推送事件
emitter.send("实时消息:你好!");
Thread.sleep(1000); // 模拟延时
emitter.send("实时消息:更新来了!");
emitter.complete(); // 结束推送
} catch (Exception e) {
emitter.completeWithError(e); // 异常处理
}
}).start();
return emitter;
}
}
客户端代码
const eventSource = new EventSource("/sse");
// 处理服务器推送的消息
eventSource.onmessage = function(event) {
console.log(event.data);
};
// 处理连接关闭或错误
eventSource.onerror = function() {
console.log("连接出现问题,自动重连...");
};
效果预览
图片
为什么会这么多,那是因为客户端的自动重连机制,无需我们手动维护,客户端会自动发起重连。
SseEmitter
其实sse的核心,就是SseEmitter这个类,是 Spring 提供的一个类,用于处理 Server-Sent Events (SSE)。它允许服务器端以流的形式推送事件给客户端,而不需要客户端不断轮询服务器。
1.构造函数
- SseEmitter():创建一个默认超时时间的 SseEmitter 实例。默认超时为 30 秒。
- SseEmitter(Long timeout):创建一个带有自定义超时时间的 SseEmitter 实例。
timeout:指定以毫秒为单位的超时时间。如果设置为 0L,则连接永远不会超时。
2.核心方法
- send(Object object):向客户端发送一条消息。
object:要发送的数据,可以是任何类型的对象。
此方法会将数据直接发送到客户端,并在响应体中流式返回。
- send(SseEmitter.SseEventBuilder event):以事件构建器的形式发送一条消息。
SseEventBuilder 是用来构建发送事件的一个内部类,允许你自定义事件的各个属性,如 id、data、name 等。
complete():表示 SSE 流完成并关闭连接。服务器告诉客户端,事件流已经结束。
completeWithError(Throwable ex):在发生错误时关闭连接,并以错误的形式告知客户端。
3.回调函数
- onCompletion(Runnable callback):指定当 SSE 连接完成(正常关闭)时执行的回调函数。
- onTimeout(Runnable callback):指定当连接超时时执行的回调函数。
- onError(Consumer<Throwable> callback):指定当发生错误时执行的回调函数。这个错误可能是由于网络连接问题、客户端断开等原因。
4.SseEventBuilder 内部类
SseEmitter.SseEventBuilder 是用于构建 SSE 事件的一个类,允许自定义事件的各个部分:
- SseEmitter.event():返回一个新的 SseEventBuilder 实例,用于构建事件。
- id(String id):设置事件的唯一标识符。客户端可以通过这个 ID 识别和处理事件。
- name(String name):设置事件的名称。客户端可以通过这个名称识别不同类型的事件,SSE 响应中会显示为 event: name。
- data(Object data):设置要发送的数据。可以是文本、JSON 等类型,最终会在客户端的 SSE 流中显示为 data: xxx。
- reconnectTime(long milliseconds):告诉客户端在多少毫秒后尝试重新连接。如果连接中断,客户端会在指定时间后自动重连。
- comment(String comment):向客户端发送一条注释(不会触发事件)。
目前 SseEmitter 是基于 每个客户端请求独立管理 的对象,因此不适合将其直接交由 Spring 管理为单例或共享对象。
每次请求应手动创建新的 SseEmitter 实例,并配置合适的超时时间。对于每个连接,SseEmitter 都是短暂的,使用完毕后应该调用 complete() 或 completeWithError() 方法来释放资源。
SSE 与 WebSocket 对比
图片
小结
- 相比轮询,SSE 通过长连接减少了网络开销和服务器压力。
- SseEmitter 适用于需要服务器实时推送数据的场景,特别是实时通知、动态更新等需求
- 相比websocket,在某些场景下,SSE 更加易用,且占用的资源较少