随着 ChatGPT 的兴起,流式输出技术逐渐走进大众视野。在技术社区中,很多开发者开始研究和实践 SSE(服务器推送事件)。不过,SSE 仍然存在一定的局限性,而 Spring 提供的 ResponseBodyEmitter 则是一种更简便的异步流式输出方式。虽然 ResponseBodyEmitter 并非新技术,它早在 Spring Framework 4.2 版本就已经引入,但在实际开发中仍然具有极高的实用价值,特别是在处理实时数据推送时。
ResponseBodyEmitter 的作用
ResponseBodyEmitter 主要用于异步 HTTP 响应处理,其核心优势在于 支持逐步向客户端发送数据,而不是一次性返回所有内容。这种特性特别适用于流式传输或需要长时间处理的任务,例如:
- 长轮询服务器保持连接开放,待有数据时立即返回。
- 服务器推送事件(SSE)持续向客户端推送更新信息。
- 流式传输逐步推送大量数据,如文件下载或实时数据流。
- 异步处理在后台执行耗时任务,并实时返回处理结果。
典型应用场景
在实际业务中,ResponseBodyEmitter 可广泛应用于:
- 实时进度更新(例如文件上传进度条)
- 实时聊天
- 股票价格推送
- AI 实时响应
- 服务器日志流式输出
实战:构建实时日志流
假设我们需要实现一个实时日志查看功能,服务器可以不断地向客户端推送最新的日志信息。
创建控制器
首先,在 Spring Boot 3.4 应用中创建 LogController,使用 ResponseBodyEmitter 处理流式日志输出。
package com.icoderoad.controller;
importorg.springframework.http.MediaType;
importorg.springframework.web.bind.annotation.GetMapping;
importorg.springframework.web.bind.annotation.RequestMapping;
importorg.springframework.web.bind.annotation.RestController;
importorg.springframework.web.servlet.mvc.method.annotation.ResponseBodyEmitter;
@RestController
@RequestMapping("/api/log")
publicclassLogController{
@GetMapping(value ="/stream", produces =MediaType.TEXT_EVENT_STREAM_VALUE)
publicResponseBodyEmitterstreamLogs(){
ResponseBodyEmitter emitter =newResponseBodyEmitter();
newThread(()->{
try{
while(true){
String logEntry =getLatestLogEntry();
if(logEntry !=null){
emitter.send(logEntry);
}
Thread.sleep(1000);// 每秒推送一次
}
}catch(Exception e){
emitter.completeWithError(e);
}
}).start();
return emitter;
}
privateStringgetLatestLogEntry(){
return"2025-02-12 12:00:00 - INFO: 用户成功登录。";
}
}
运行效果:
访问 http://localhost:8080/api/log/stream,服务器将每秒推送一条新的日志条目,前端可以实时接收并展示。
ResponseBodyEmitter 的核心方法
- send(Object data)逐步向客户端发送数据,可多次调用。
- complete()结束响应流。
- onTimeout(Runnable callback)连接超时时触发回调。
- onCompletion(Runnable callback)数据发送完成后触发回调。
ResponseBodyEmitter 的工作原理
异步数据生成与推送
在传统 HTTP 响应模式中,服务器通常需要等待整个响应数据生成完成后再返回。而 ResponseBodyEmitter 允许 异步逐步推送数据,即部分数据准备好后立即 send() 给客户端,提高响应速度。
分块传输(Chunked Encoding)
传统 HTTP 响应头需要指定 Content-Length,但 ResponseBodyEmitter 采用 分块传输(Chunked Encoding),无需指定数据总长度,而是将数据分块推送,客户端收到后即可立即处理,减少等待时间。
连接生命周期管理
- 数据发送完毕时调用 complete() 关闭连接,避免资源浪费。
- 若发生异常,调用 completeWithError(),通知客户端连接关闭。
注意事项
- 客户端支持:大多数现代浏览器和 HTTP 客户端都支持 ResponseBodyEmitter,但部分旧版本可能存在兼容性问题。
- 超时管理:建议设置超时时间,避免长连接占用资源,例如:
emitter.onTimeout(() -> emitter.complete());
- 线程安全:send() 方法是线程安全的,但仍需合理管理任务线程,避免资源泄漏。
- 连接关闭:务必调用 complete() 或 completeWithError() 以释放资源。
ResponseBodyEmitter 与其他流式技术对比
技术方案 | 适用场景 | 优缺点 |
Streaming | 低级字节流传输 | 灵活性高,但需要手动管理 |
SSE | 服务器推送事件 | 需浏览器支持 |
ResponseBodyEmitter | 通用 HTTP 流式传输 | 兼容性更好,易于与 Spring 集成 |
在 AI 流式输出等场景下,ResponseBodyEmitter 相比 SSE 具有更好的 HTTP 兼容性,且使用方式更灵活。
结语
ResponseBodyEmitter 是 Spring 提供的一种轻量级流式传输解决方案,非常适用于 高并发、实时数据推送 需求。无论是 进度条实时更新、实时聊天、股票数据推送、系统日志流式输出,它都能提供更丝滑的用户体验。如果你的应用需要高效的异步数据推送,不妨试试 ResponseBodyEmitter 吧!