Spring WebFlux中WebClient处理延迟Flux的差异行为咨询
Spring WebFlux Flux响应行为差异解析
问题场景回顾
定义了两个Flux端点:
/customObjectsAsString:返回Flux<String>,每个元素延迟3秒发射/customObjects:返回Flux<CustomObject>,每个元素延迟3秒发射
测试发现:
- Chrome访问
/customObjectsAsString时,每隔3秒依次显示字符串;访问/customObjects时,等待6秒后一次性显示完整JSON数组 - WebClient测试中,两个接口均需等待6秒后一次性接收所有元素
1. 字符串形式与JSON形式发射元素的差异
核心差异在于媒体类型和序列化逻辑:
- 字符串Flux:默认使用
text/plain媒体类型,Spring WebFlux会将每个String元素作为独立文本块逐块发送,无需额外结构包裹,每个元素可单独被识别。 - 自定义对象Flux:默认使用
application/json媒体类型,Jackson序列化器会把整个Flux序列化为标准JSON数组(格式[{}, {}])。由于JSON数组必须以]闭合才是有效格式,服务端需等待所有元素生成完毕才能完成序列化,因此一次性返回全部数据。
若要让自定义对象也能逐元素发送,需启用JSON流模式,指定媒体类型为application/stream+json,此时每个对象会被单独序列化,以换行分隔,而非包裹在数组中。
2. WebClient无法逐元素接收的原因及解决方法
WebClient的行为由响应媒体类型和消息转换器的处理逻辑决定:
- 对于
/customObjects(JSON数组):bodyToFlux(CustomObject.class)依赖Jackson解析完整JSON数组,只有当整个数组接收完成后,才能拆分出多个CustomObject实例,因此一次性返回所有元素。 - 对于
/customObjectsAsString(纯文本):默认的text/plain消息转换器会把整个响应体拼接成一个String后再发射给Flux,而非逐块拆分。
解决代码示例
修改自定义对象接口为JSON流模式
import org.springframework.http.MediaType; @GetMapping(value="/customObjects", produces = MediaType.APPLICATION_STREAM_JSON_VALUE) public Flux<CustomObject> getCustomObjects() { CustomObject customObjectOne = CustomObject.builder().text("One").number(1).build(); CustomObject customObjectTwo = CustomObject.builder().text("Two").number(2).build(); return Flux.just(customObjectOne, customObjectTwo).delayElements(Duration.ofSeconds(3)); }
修改WebClient字符串接口请求为事件流模式
Flux<String> flux = WebClient.builder().baseUrl("http://localhost:8080").build().get() .uri("/customObjectsAsString") .accept(MediaType.TEXT_EVENT_STREAM) .retrieve().bodyToFlux(String.class);
修改后,WebClient就能每隔3秒接收一个元素,日志会显示不同的时间戳。
3. 浏览器表现差异的原因
浏览器对不同媒体类型的响应处理逻辑不同:
/customObjectsAsString(text/plain):浏览器会实时渲染接收到的文本内容,每收到一块新文本就追加显示在页面上,因此能看到每隔3秒出现新内容。/customObjects(application/json):浏览器会等待完整的有效JSON结构(闭合的数组)加载完成后,才会显示格式化的JSON内容。如果改为application/stream+json,浏览器会接收到换行分隔的JSON对象,但默认不会自动逐行解析显示,需要前端JavaScript通过ReadableStream处理流数据才能实现实时展示。
内容的提问来源于stack exchange,提问作者harish ch
相关产品推荐
相关产品推荐

