news 2026/4/16 15:10:06

SpringBoot + ResponseBodyEmitter 实时异步流式推送

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
SpringBoot + ResponseBodyEmitter 实时异步流式推送

目前市面上异步推送大多用的是websocket或者http轮训,今天咱们换一种更为简便的方式来实现流式输出,那就是ResponseBodyEmitter

其实,ResponseBodyEmitter并非新技术,早在 Spring Framework 4.2 版本就已被引入。直到最近,我们在开发一个滚动日志输出功能时,才深入了解到它的强大之处。

ResponseBodyEmitter 的作用

相较于 websocket 技术,ResponseBodyEmitter更加简单易用。它主要用于处理异步的 HTTP 响应,其核心优势在于允许逐步将数据发送到客户端,而非一次性发送所有内容。这一特性使得它在需要长时间处理或进行流式传输的场景中表现出色。需要注意的是,ResponseBodyEmitter本质上是一个接口。

使用场景

  1. 长轮询:服务器在有数据时会立即响应客户端请求,若暂无数据,则保持连接开放,等待数据到来。
  2. 服务器推送事件 (SSE):服务器能够持续不断地向客户端推送各类事件,实现实时交互。
  3. 流式传输:可逐步发送大量数据,像文件下载或者实时数据流传输等场景都适用。
  4. 异步处理:在处理耗时任务时,能逐步返回处理结果,避免客户端长时间等待,提升用户体验。

业务场景举例

在实际业务中,ResponseBodyEmitter有着广泛的应用,比如进度条的实时更新、实时聊天功能、股票价格的实时更新、系统日志的流式输出以及 AI 的流式响应等。

实时日志流实战

接下来,我们通过一个简单的实时日志流功能,来深入了解ResponseBodyEmitter的使用。假设我们有一个应用程序,需要实时查看服务器的日志,以便快速定位和解决问题。

创建控制器

首先,我们在 Spring Boot 应用中创建一个控制器,借助ResponseBodyEmitter实现实时日志流。

import org.springframework.http.MediaType; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyEmitter; @RestController @RequestMapping("/api/log") publicclass LogController { @GetMapping(value = "/stream", produces = MediaType.TEXT_EVENT_STREAM_VALUE) public ResponseBodyEmitter streamLogs() { ResponseBodyEmitter emitter = new ResponseBodyEmitter(); // 开启异步线程处理数据并发送 new Thread(() -> { try { while (true) { String logEntry = getLatestLogEntry(); if (logEntry != null) { emitter.send(logEntry); } // 每秒检查一次日志更新 Thread.sleep(1000); } } catch (Exception e) { // 出现异常时结束响应并传递错误信息 emitter.completeWithError(e); } }).start(); return emitter; } private String getLatestLogEntry() { // 模拟从日志文件中获取最新日志条目 return"2025-02-12 12:00:00 - INFO: User logged in successfully."; } }

运行效果

当我们启动这个应用程序,并访问/api/log/stream路径时,就能看到一个实时更新的日志流。服务器会每秒向客户端推送一条新的日志条目,客户端会将其显示在页面上,效果如下:

ResponseBodyEmitter 的核心方法

  • send(Object data):向客户端发送数据,该方法可以多次调用,实现数据的逐步发送。
  • complete():用于结束响应流,表示数据已经全部发送完毕。
  • onTimeout(Runnable callback):设置超时回调函数,当连接超时时,会执行该回调。
  • onCompletion(Runnable callback):设置完成回调函数,当数据发送完成后,会执行该回调。

ResponseBodyEmitter 工作原理

异步数据生成与推送

在传统的 HTTP 请求 - 响应模式中,服务器通常需要等待整个响应数据生成完成后,才会将其一次性发送给客户端。而ResponseBodyEmitter打破了这种模式,它允许服务端在任务执行过程中异步地生成响应数据。

当有部分数据准备好时,就可以立即调用send()方法将这些数据推送给客户端,而无需等待整个任务完成。这就好比一场接力赛,每完成一段赛程(生成一部分数据),就马上将接力棒(数据)传递给客户端,大大提高了数据传输的实时性。

分块传输机制

ResponseBodyEmitter采用了 HTTP 的分块编码(Chunked Encoding)方式来传输数据。在传统的 HTTP 响应中,通常需要在响应头中明确指定Content-Length,表示整个响应数据的长度。但在分块传输中,服务器不会提前设置Content-Length,而是将数据分成多个独立的块,每个块都有自己的长度标识。

客户端在接收到数据块后,可以立即对其进行处理,而不必等待整个响应数据接收完毕。这种方式使得数据可以边生成边传输,减少了客户端的等待时间,提高了用户体验。

连接生命周期管理

为了确保资源的合理使用,ResponseBodyEmitter提供了对连接生命周期的有效管理。当所有数据都发送完毕后,需要调用complete()方法来明确告知客户端响应结束,关闭连接。如果在数据传输过程中出现异常,可以调用completeWithError()方法,结束响应并向客户端传递错误信息。

这样可以避免连接长时间保持开放,造成资源浪费。

注意事项

  1. 客户端支持:虽然大多数浏览器和 HTTP 客户端库都支持分块传输,但某些老旧的客户端可能存在兼容性问题。
  2. 超时设置:为避免长连接长时间占用资源,可以为ResponseBodyEmitter设置超时时间,示例代码如下:
emitter.onTimeout(() -> emitter.complete());
  1. 线程安全ResponseBodyEmittersend()方法是线程安全的,但在使用时需要注意控制任务线程的生命周期,避免出现资源泄漏。
  2. 连接关闭:务必确保在任务结束时调用complete()completeWithError()方法,否则可能导致连接无法正常关闭,造成资源浪费。

小结

ResponseBodyEmitter是 Spring 框架提供的轻量级流式传输解决方案,它能够显著提升高并发和实时性场景下的用户体验。通过ResponseBodyEmitter,我们可以轻松实现服务器向客户端的实时数据推送。

无论是进度条的实时更新、实时聊天、股票价格的实时监控还是系统日志的流式输出,ResponseBodyEmitter都能帮助我们构建更加动态和互动的应用程序。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/4/16 14:28:12

基于springboot架的超市进销存管理系统的设计与实现

目录 摘要关键词 开发技术路线结论源码lw获取/同行可拿货,招校园代理 :文章底部获取博主联系方式! 摘要 该系统基于SpringBoot框架开发,结合MySQL数据库,设计并实现了一套完整的超市进销存管理系统。系统采用B/S架构,…

作者头像 李华
网站建设 2026/4/16 14:28:19

Spring Boot中实现多线程6种方式,提高架构性能

Spring Boot 中实现多线程的 6 种主流方式(2025-2026 实战指南) Spring Boot 提供了非常丰富的多线程支持手段,从最简单的注解到虚拟线程(Java 21 / 25 时代的主流方向),可以满足从简单异步任务到高并发 I…

作者头像 李华
网站建设 2026/4/16 12:13:25

C#x2B;#x2B;小白训练第十三天

C小白训练第十三天 以下为牛客挑战 今日收获 vector<pair<int,int>>v;用于存储坐标&#xff0c;如果坐标&#xff1a;方式&#xff1a;v.push_back({i,j}),v.emplace_back(i,j);v.push_back(make_pair(i, j));dp联想的又一个条件&#xff0c;就是因为限制只存在与…

作者头像 李华
网站建设 2026/4/11 12:15:07

照着用就行:10个降AI率工具测评,专科生必看的降AI率指南

在当前的学术写作环境中&#xff0c;AI生成内容&#xff08;AIGC&#xff09;已经成为许多学生和研究者不得不面对的问题。尤其是对于专科生来说&#xff0c;如何有效降低论文中的AI痕迹、提升原创性&#xff0c;是确保论文顺利通过查重检测的关键步骤。随着AI技术的普及&#…

作者头像 李华
网站建设 2026/4/15 4:59:47

综合项目(一):KingbaseES 数据库表结构设计

综合项目&#xff08;一&#xff09;&#xff1a;KingbaseES 数据库表结构设计 ——一个老架构师的“别再用 MySQL 思维搞国产数据库”的血泪忠告&#xff1a;在电科金仓支撑的学生管理系统里&#xff0c;乱建表 数据泄露 性能雪崩 国产化验收翻车&#xff01; 开场白&#…

作者头像 李华