ResponseBodyEmitter 使用指南
📖 前言
最近在学习 AI Agent 开发,在前后端数据交互的时候遇到这样一个问题:如果使用者向 AI 发送一段提示词,AI 通常的处理完毕的耗时会比较长,因为他是流式输出的,这时候,前端只能静悄悄的等待,给使用者带来的体验就不是很美妙。毕竟,在你讲完话之后,对面静悄悄的,跟死了一样,肯定不是很美妙了,我们需要相对应的流式交互方式来解决这一点,后端的 AI 产生一点内容就向前端输出一点内容,这样就比较美妙了。
🎯 解决问题
适合耗时比较长的任务或者是会持续输出内容的任务,比如下载进度、实时日志、大数据量分片加载等等。
ResponseBodyEmitter:是 Spring MVC(从 4.2 版本引入)提供的一个工具,用于异步、分段地向客户端发送数据
特点:
- 不阻塞服务器线程:在数据准备好之前,释放容器线程去处理其他请求
- 流式输出:数据产生一点,就发给客户端一点
非常美妙...
💡 简单示例
@GetMapping("/stream")
public ResponseBodyEmitter handle() {
// 1. 创建一个发射器(可以设置超时时间)
ResponseBodyEmitter emitter = new ResponseBodyEmitter();
// 2. 启动异步线程发送数据
Executors.newSingleThreadExecutor().execute(() -> {
try {
for (int i = 0; i < 5; i++) {
// 模拟耗时操作
Thread.sleep(1000);
// 发送数据
emitter.send("第 " + i + " 批数据包\n");
}
// 3. 全部发送完成
emitter.complete();
} catch (Exception e) {
emitter.completeWithError(e);
}
});
return emitter;
}可以看出,我们可以直接返回 ResponseBodyEmitter 对象,然后,接受者和发送者之间像是建立了一个管道一样,我们可以让另外一个线程使用创建好的这个对象可以发送数据,另一端会逐步的接收数据,直到通道关闭。
📚 基本用法
文档详见: ResponseBodyEmitter (Spring Framework 7.0.3 API)
构造方法
ResponseBodyEmitter()- 常用ResponseBodyEmitter(Long timeout)- 设置超时时间
常用方法
send(Object data)- 向客户端发送数据,可以多次调用complete()- 结束响应流,表示数据发送完毕onTimeout(Runnable callback)- 设置超时回调函数onCompletion(Runnable callback)- 设置完成回调函数
🔧 工作原理
控制器返回对象:Controller 不再返回普通的
String或ResponseEntity,而是直接返回ResponseBodyEmitter实例句柄持有:Spring 会保持这个 HTTP 连接开启
异步发送:你可以把这个
emitter丢给另一个异步线程。那个线程只要调用emitter.send(data),数据就会被推送到客户端完成或超时:调用
emitter.complete()关闭连接,或者因为超时自动断开
⚠️ 注意事项
- 资源释放:注意用完就丢掉或者直接设置超时时间,超时就关闭
- 线程安全:
send()方法是线程安全的
🔄 相关工具对比
在 Spring 中,你可能还会看到另外两个长得很像的东西:
| 工具 | 特点 | 适用场景 |
|---|---|---|
| ResponseBodyEmitter | 通用的异步流 | 灵活地发送任何对象,Spring 会用 HttpMessageConverter 转成 JSON 等 |
| SseEmitter | 继承自上面那位,专门用于 Server-Sent Events (SSE) | 浏览器端的实时推送(类似单向 WebSocket) |
| StreamingResponseBody | 性能更高,直接操作 OutputStream | 适合发送大文件或纯文本流 |
👋 总结
ResponseBodyEmitter 是一个非常实用的工具,特别适合需要流式输出的场景。通过异步发送数据,可以大大提升用户体验,避免长时间等待的尴尬。
掰掰~