摘要
本文介绍了如何实现SSE事件流的接收和处理,通过使用Spring自身非常兼容的三大组件来实现,让我们开始。
三个核心组件
SseEmitter + WebClient + Flux
- SseEmitter
在服务器端事件(SSE, Server-Sent Events)场景中,SSE的响应格式通常是text/event-stream,并且服务器会持续向客户端发送事件流,而不是一次性返回所有结果。SseEmitter是Spring提供的用于实现SSE的核心类,它可以保持与客户端的长连接,并逐步发送数据,确保每条数据都能及时推送到客户端。
- WebClient
WebClient是Spring WebFlux提供的响应式HTTP客户端,它是响应式、非阻塞的,支持异步和流式处理,非常适合SSE场景。使用WebClient + SseEmitter实现服务端推送。可以充分利用Spring的响应式编程能力,实现高效、稳定的流式通信。适用于AI聊天、实时日志、数据流等场景
- Flux
Flux是Reactor库中的一个核心概念,用于处理异步的、基于事件的响应式编程模型。Flux表示一个异步的序列数据流,可以是零个或多个数据项,甚至可以是无限流,并且可以处理各种事件(如数据项、错误、完成等)。Flux的主要作用是处理多个数据项的连续传输,这与SSE场景非常契合,因为SSE就是典型的服务器向客户端发送连续事件流。
逻辑解析
- 构建Web请求
通过WebClient发送http请求,并设置请求头和请求体并将响应体解析为Flux,表示一个可订阅的字符串流。
- 获取响应流
通过.retrieve()方法获取响应,并通过.bodyToFlux(String.class)将响应体转换为Flux。这表示响应体将被解析为一个字符串序列流。
- 订阅流
通过.subscribe()方法订阅这个流,处理流中每个数据项。每当服务器发送一个新的数据项(例如SSE事件),subscribe方法中的回调函数就会被触发,处理这个数据项。
data -> {}:处理流中的每个数据项,每当服务器发送一个新的数据项,这个回调函数就会被触发。
error -> {}:处理流中的错误事件。
() -> {}:处理流的完成事件,当服务器关闭连接时触发。
- SseEmitter超时设置
Spring Boot2.2.0及之后,默认超时时间是60秒。
可通过new SseEmitter(Long timeout)设置
- 实时推送数据
订阅Flux,在每次收到数据时都实时推送给客户端。
SseEmitter.send(...)
- 异步执行请求
使用线程池异步执行HTTP请求,避免阻塞主线程。
executorService.execute(() -> {...})
- 异常处理与连接关闭
无论成功还是失败,最终都要终止连接,确保资源释放。
emitter.complete()
emitter.completeWithError(...)
代码示例
事件流示例:

kotlin
-- 返回示例
已连接到 http://xxx
data: {"字段名1": "内容", "字段名2": "内容", "字段名3": "内容"}
data: {"字段名1": "内容", "字段名2": "内容", "字段名3": "内容"}
data: {"字段名1": "内容", "字段名2": "内容", "字段名3": "内容"}
已断开连接 http://xxx
导入依赖:
xml
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webflux</artifactId>
</dependency>
核心代码:
java
package org.coffeebeans.sse;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Service;
import org.springframework.web.reactive.function.client.WebClient;
import org.springframework.web.servlet.mvc.method.annotation.SseEmitter;
import reactor.core.publisher.Flux;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.*;
/**
* <li>ClassName: SseService </li>
* <li>Author: OakWang </li>
*/
@Service
@Slf4j
public class SseService {
private final ExecutorService executorService = Executors.newCachedThreadPool();
private final WebClient webClient = WebClient.builder().baseUrl("http://xxx").build();
public SseEmitter test() {
// 设置请求头
String authorization = "Bearer xxx";
// 初始化参数
Map<String, Object> params = new HashMap<>();
params.put("name", "xxx");
// 转换为JSON字符串
ObjectMapper objectMapper = new ObjectMapper();
String jsonRequest;
try {
jsonRequest = objectMapper.writeValueAsString(params);
} catch (JsonProcessingException e) {
log.error("JSON转换失败:", e.getMessage());
throw new RuntimeException("JSON 转换失败");
}
SseEmitter emitter = new SseEmitter(60000L); // 设置连接超时时间为60秒
// 使用线程池异步发送数据
executorService.execute(() -> {
try {
// 构造请求参数
Flux<String> flux = webClient.post()
.uri("/xxx")
.header("Authorization", authorization)
.header("Content-Type", MediaType.APPLICATION_JSON_VALUE)
.bodyValue(jsonRequest) // 构造 JSON 请求体
.retrieve() // 获取响应
.bodyToFlux(String.class); // 将响应体转换为 Flux<String>,解析为一个字符串序列流
flux.subscribe(
data -> { // 处理每个数据项
try {
emitter.send(SseEmitter.event().data(data)); // 实时推送
} catch (IOException e) {
log.error("推送数据失败", e);
emitter.completeWithError(e);
}
},
error -> { // 处理错误
try {
emitter.send(SseEmitter.event().data(error.getMessage()));
} catch (IOException e) {
log.error("推送数据失败", e);
throw new RuntimeException(e);
} finally {
emitter.completeWithError(error);
}
},
() -> { // 处理完成事件
emitter.complete(); // 数据流结束
}
);
} catch (Exception e) {
log.error("初始化流式请求失败", e);
try {
emitter.send(SseEmitter.event().data(e.getMessage()));
} catch (IOException ex) {
emitter.completeWithError(e);
}
emitter.completeWithError(e);
}
});
return emitter;
}
}
总结
以上我们了解了SSE响应流的处理方式,通过三个核心组件:SseEmitter、WebClient和Flux共同实现高效的服务器端事件(SSE)实时推送功能。
关注公众号:咖啡Beans
在这里,我们专注于软件技术的交流与成长,分享开发心得与笔记,涵盖编程、AI、资讯、面试等多个领域。无论是前沿科技的探索,还是实用技巧的总结,我们都致力于为大家呈现有价值的内容。期待与你共同进步,开启技术之旅。