SpringBoot使用ResponseBodyEmitter处理流式日志和进度条

 更新时间:2025年02月14日 11:02:06   作者:码猿技术专栏  
这篇文章主要为大家详细介绍了SpringBoot如何使用ResponseBodyEmitter处理流式日志和进度条,感兴趣的小伙伴可以跟随小编一起学习一下

自从 ChatGPT 火了之后,带动了大批量的流式输出的使用。

在流式输出火的那段时间,不少爱钻研技术的小伙伴们,都学习并上手了 SSE 异步处理。我那时也写了一篇文章,今天我们换一种更简单的方式,来实现流式输出。

它就是 ResponseBodyEmitter。它并不是一个新技术,早在 Spring Framework 4.2 版本中就被引入了。直到最近我们需要做一个滚动的日志输出功能,才了解到它。

作用

相比 SSE 技术,ResponseBodyEmitter 更简单。它常用于处理异步的 HTTP 响应,允许逐步发送数据到客户端,而不是一次性发送所有内容。它适用于需要长时间处理或流式传输的场景。

需要注意的是,ResponseBodyEmitter 只是一个接口。

使用场景

  • 长轮询:服务器在有数据时立即响应,否则保持连接开放。
  • 服务器推送事件 (SSE):服务器可以持续向客户端推送事件。
  • 流式传输:逐步发送大量数据,如文件下载或实时数据流。
  • 异步处理:处理耗时任务时,逐步返回结果,避免客户端长时间等待。

业务举例

进度条、实时聊天、股票价格更新、系统日志流、AI 的流式输出等。

实时日志流

实战演练:实现一个简单的实时日志流。

为了让这个概念更加具体,我们来实现一个简单的实时日志流功能。假设你有一个应用程序,需要实时查看服务器的日志,以便快速定位和解决问题。

创建控制器

首先,我们在 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")
public class 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 工作原理

  • 服务端异步生成响应数据:任务执行时,调用 send() 方法将数据推送至客户端。
  • 分块传输:数据以 HTTP 的**分块编码(Chunked Encoding)**方式传输,不会提前设置Content-Length,而是分段发送数据块。
  • 连接生命周期:通过complete()completeWithError()控制连接的关闭。

注意事项

支持的客户端:大多数浏览器和 HTTP 客户端库支持分块传输,但某些老旧的客户端可能不支持。

超时设置:为了避免长连接占用资源,可以为ResponseBodyEmitter设置超时时间:

emitter.onTimeout(() -> emitter.complete());

线程安全:ResponseBodyEmitter 的 send() 方法是线程安全的,但需要注意控制任务线程的生命周期。

连接关闭:需要确保任务结束时调用 complete() 或 completeWithError(),否则可能导致资源泄露。

与 Streaming 和 SSE 的对比

  • Streaming:直接通过 OutputStream 向客户端写入数据,灵活性高,但需手动处理流的关闭。
  • Server-Sent Events (SSE):基于 text/event-stream,适用于服务端事件推送,客户端需支持 SSE。
  • ResponseBodyEmitter:更通用,适用于任何支持 HTTP 的客户端,且易于与 Spring 集成。

类似 AI 这样的响应式的、流式输出,相比 SSE 而言,ResponseBodyEmitter 是 Spring 提供的轻量级流式传输解决方案,同时 http 协议兼容性更好。

小结

ResponseBodyEmitter 是 Spring 提供的轻量级流式传输解决方案,能有效提升高并发和实时性场景的用户体验。通过 ResponseBodyEmitter,我们可以轻松实现服务器向客户端的实时数据推送。无论是进度条、实时聊天、股票价格更新还是系统日志流,ResponseBodyEmitter 都能帮助我们构建更加动态和互动的应用程序。

到此这篇关于SpringBoot使用ResponseBodyEmitter处理流式日志和进度条的文章就介绍到这了,更多相关SpringBoot ResponseBodyEmitter处理日志内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • Spring session实现共享单点登录案例过程解析

    Spring session实现共享单点登录案例过程解析

    这篇文章主要介绍了Spring session实现共享单点登录案例过程解析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
    2020-07-07
  • Spring超详细讲解AOP面向切面

    Spring超详细讲解AOP面向切面

    面向对象编程是一种编程方式,此编程方式的落地需要使用“类”和 “对象”来实现,所以,面向对象编程其实就是对 “类”和“对象” 的使用,面向切面编程,简单的说,就是动态地将代码切入到类的指定方法、指定位置上的编程思想就是面向切面的编程
    2022-10-10
  • Spring Boot中利用JavaMailSender发送邮件的方法示例(附源码)

    Spring Boot中利用JavaMailSender发送邮件的方法示例(附源码)

    这篇文章主要介绍了Spring Boot中利用JavaMailSender发送邮件的方法示例, 相信使用过Spring的众多开发者都知道Spring提供了非常好用的JavaMailSender接口实现邮件发送。在Spring Boot的Starter模块中也为此提供了自动化配置。需要的朋友可以参考借鉴。
    2017-02-02
  • SpringBoot用JdbcTemplates操作Mysql实例代码详解

    SpringBoot用JdbcTemplates操作Mysql实例代码详解

    JdbcTemplate是Spring框架自带的对JDBC操作的封装,目的是提供统一的模板方法使对数据库的操作更加方便、友好,效率也不错,这篇文章主要介绍了SpringBoot用JdbcTemplates操作Mysql
    2022-10-10
  • java awt实现计算器功能

    java awt实现计算器功能

    这篇文章主要为大家详细介绍了java awt实现计算器功能,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2020-12-12
  • Java单例模式中的线程安全问题

    Java单例模式中的线程安全问题

    本文主要介绍了Java单例模式中的线程安全问题,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2022-06-06
  • java实现数字转大写的方法

    java实现数字转大写的方法

    这篇文章主要介绍了 java实现数字转大写的方法的相关资料,希望通过本文能帮助到大家,让大家实现这样的功能,需要的朋友可以参考下
    2017-10-10
  • Redis 订阅发布_Jedis实现方法

    Redis 订阅发布_Jedis实现方法

    下面小编就为大家带来一篇Redis 订阅发布_Jedis实现方法。小编觉得挺不错的,现在就分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2017-06-06
  • SpringBoot快速整合SpringSecurity的详细步骤(新手都会!)

    SpringBoot快速整合SpringSecurity的详细步骤(新手都会!)

    日 Spring Security 是针对Spring项目的安全框架,也是Spring Boot底层安全模块默认的技术选型,他可以实现强大的Web安全控制,下面这篇文章主要给大家介绍了关于SpringBoot快速整合SpringSecurity的详细步骤,需要的朋友可以参考下
    2023-03-03
  • 浅谈java获取UUID与UUID的校验

    浅谈java获取UUID与UUID的校验

    这篇文章主要介绍了java获取UUID与UUID的校验,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2019-03-03

最新评论