1. 在Web应用中,流式输出是优化大数据量传输和内存使用的关键技术。传统阻塞式Servlet架构需将全部数据加载到内存后再响应,易引发内存溢出。而响应式编程(如WebFlux)和流式API(如StreamingResponseBody)支持逐条处理数据,边查询边输出,显著降低资源占用。本文通过四种场景对比:
伪流式(阻塞式+Flux包装List)
内存流式(阻塞式+StreamingResponseBody)
真流式(阻塞式+数据库流查询)
响应式流式(WebFlux+R2DBC)
2. 实战案例
2.1 基于Servlet技术(阻塞式)
我们引入了以下两个依赖:其中 spring-boot-starter-webflux 用于支持在 Controller 接口中返回 Flux 类型的响应式流数据。
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-webflux</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency>
@Service("blockUserService")
public class UserService
{
private final UserRepository ur;
public List < User > queryUsers()
{
return this.ur.findAll();
}
}
@GetMapping(produces = MediaType.TEXT_EVENT_STREAM_VALUE) public Flux < User > flux(HttpServletResponse response) { response.setCharacterEncoding("UTF-8"); return Flux.fromIterable(this.userService.queryUsers()).delayElements(Duration.ofSeconds(1)); }
该接口返回值为Flux,以实现流式输出。
运行结果

2.2 使用StreamingResponseBody
在 UserService#queryUsers 方法中,当前直接返回 List 集合。我们可改用 StreamingResponseBody 实现流式输出。如下示例:
@GetMapping(path = "/stream") public StreamingResponseBody stream(HttpServletResponse response) { response.setContentType("text/event-stream;charset=utf-8"); return outputStream - > { userService.queryUsers().forEach(user - > { try { String json = objectMapper.writeValueAsString(user) + "\n" ; outputStream.write(json.getBytes()); outputStream.flush(); TimeUnit.SECONDS.sleep(1) ; } catch (Exception e) { e.printStackTrace(); } }) ; } ; }
运行结果

2.3 流式查询输出
在上面的示例中,我们是先将所有数据查询到内存中再通过流的方式输出数据,这样会带来内存溢出的风险,接下来的示例我们将通过流的方式查询数据,然后在通过StreamingResponseBody 流式输出到客户端。
Repository
@Query("SELECT u FROM User u")
Stream < User > queryUsers() ;
Service
@Transactionalpublic void streamQuery(Consumer < User > consumer) { try (Stream < User > stream = this.ur.queryUsers()) { stream.forEach(user - > { consumer.accept(user); }); } }
Controller接口
@GetMapping(path = "/real/stream") public StreamingResponseBody realStream(HttpServletResponse response) { response.setContentType("text/event-stream;charset=utf-8"); return outputStream - > { userService.streamQuery(user - > { try { String json = objectMapper.writeValueAsString(user) + "\n" ; outputStream.write(json.getBytes()); outputStream.flush(); TimeUnit.SECONDS.sleep(1); } catch (Exception e) { e.printStackTrace(); } }); }; }
运行结果

2.4 基于WebFlux技术(响应式)
引入依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webflux</artifactId>
</dependency>
<dependency>
<groupId>io.asyncer</groupId>
<artifactId>r2dbc-mysql</artifactId>
</dependency>
实体对象
@Table("x_user")
public class User
{ @Id
private Integer id;
private String name;
private String sex;
private String phone;
private Integer age;
}
Repository & Service
public interface UserRepository extends ReactiveCrudRepository < User, Integer > {} @Service public class UserService { private final UserRepository ur; public Flux < User > queryUsers() { return this.ur.findAll(); } }
Controller接口
@GetMapping(produces = "text/event-stream") public Flux < User > queryByName(ServerHttpResponse response) { // 通过delayElement模拟逐条输出效果 return this.userService.queryUsers().delayElements(Duration.ofSeconds(1)) ; }
运行结果

原文地址:https://mp.weixin.qq.com/s/WZde8gX7cJ1r56yFWrgh-w
浙公网安备 33010602011771号