Spring MVC的异步模式(ResponseBodyEmitter、SseEmitter、StreamingResponseBody)

server/2024/9/20 7:27:19/ 标签: spring, mvc, http
http://www.w3.org/2000/svg" style="display: none;">

在SpringBoot异步接口实现:提高系统的吞吐量中,讲到使用Callable、WebAsyncTask、DeferredResult来提供异步接口,但是他们仅用于返回单个异步值——即返回一个值之后,就不能再返回值了。如果想生成多个异步值并将这些值写入响应,那么可以使用HTTP的流式传输。

spring mvc中提供了3种http的流式传输:ResponseBodyEmitterSseEmitterStreamingResponseBody。只要在接口中,返回这三个对象中的一个,那么该接口就是异步接口。

特别说明一下:除了直接返回这三个对象外,返回ResponseEntity(泛型参数为三者中的一个)也是一样的效果,并且推荐使用ResponseEntity,毕竟可以设置很多http相关的信息,比如:header、contentType等等。

ResponseBodyEmitter

假如有这样一个场景:前端发起一个请求查询某些商品信息,后端根据条件查找到结果之后返回给前端。假如后端查找这些商品信息只能一条一条的找,那么找完所有的结果会耗费很长时间,并且前端也不一定想要全部的数据。就像我们问chartgpt一样,有时我们并不想看完整的答案,可能他回答到一半,我们就知道了(chartgpt的回答结果都是一点点蹦出来的,不是一下全部展示出来)。那么这个场景最好的解决方案就是,后端每找到一条数据之后,就给前端展示,前端如果不想要了,就停止接收。

这里我们先写一个接口,模拟前端请求:

@GetMapping("/events")
public ResponseEntity<ResponseBodyEmitter> handle() {ResponseBodyEmitter emitter = new ResponseBodyEmitter(0L);gEmitter = emitter;return ResponseEntity.ok().contentType(MediaType.TEXT_HTML).body(emitter);
}

在这个接口中,返回的是ResponseEntity<ResponseBodyEmitter>,当然也可以直接返回ResponseBodyEmitter。我用一个全局变量gEmitter保存了这个返回的ResponseBodyEmitter。

另外,在new ResponseBodyEmitter(0L)中,这个0或者-1代表永不超时。如果在实际使用中,发现超时等问题,请检查服务器配置、网关、nginx等。

现在再写一个接口,模拟查询商品的操作:

@GetMapping("/sendEmitter")
public void sendEmitter(@RequestParam("str") String str) throws IOException {if (Objects.equals("end", str)) {gEmitter.complete();} else {gEmitter.send(str);}
}

这个接口明显是另一个线程来处理的,在这个接口中,我们获取到之前保存的ResponseBodyEmitter对象。然后通过这个对象发送信息。我们发送消息的频率就类似查询每个符合条件商品的间隔时间。

现在浏览器访问:/events。然后用postman随意间隔的访问/sendEmitter接口,此时会发现每访问一次,访问/events的浏览器页面就会显示出相应的结果。

也就是说,ResponseBodyEmitter可以跨线程的多次向前端输出结果

SseEmitter

SseEmitter是ResponseBodyEmitter的子类,提供对服务器发送事件的支持,其中从服务器发送的事件根据 W3C SSE 规范进行格式化。两者的使用方式差不多,我在另一篇文章中,有详细的案例说明:SSE:后端向前端发送消息(springboot SseEmitter)

StreamingResponseBody

看官网中说:有时,想绕过消息转换并直接流式传输到响应OutputStream (例如,用于文件下载)很有用。您可以使用StreamingResponseBody返回值类型来执行此操作。

例如如下接口,就可以实现前端每间隔1秒输出一个fyk_test_x,x为数字:

@GetMapping("/stream2")
public ResponseEntity<StreamingResponseBody> stream() {StreamingResponseBody stream = out -> {String message = "fyk_test_";for (int i = 0; i < message.length(); i++) {try {out.write((message + i).getBytes());out.write("\r\n".getBytes());//调用一次flush就会像前端写入一次数据out.flush();TimeUnit.SECONDS.sleep(1);} catch (InterruptedException e) {e.printStackTrace();}}};return ResponseEntity.ok().contentType(MediaType.TEXT_HTML).body(stream);
}

说个题外话,在实际的使用中,我还真没有用过StreamingResponseBody。如果说是信息分段传输,那么另外两个都可以实现了。至于官方说的大文件下载,spring也有FileSystemResource对象来传输,也不会内存溢出,甚至直接以流的方式,向response.getOutputStream()写入文件流也没有问题。

另外,在测试的时候,发现StreamingResponseBody有个缺点:无法很好的修改超时时间导致超时报错。

试想一下,下载一个大文件,那么这个时间怎么定呢?同步接口下载没有超时时间一说,但是异步接口默认的超时时间一般是30秒(关于SpringBoot MVC接口超时时间的分析),30秒没有下载完,就是报错。使用StreamingResponseBody没法为这个接口单独设置超时时间,只能通过全局设置。这肯定不合理——因为这一个接口,改变了全局异步接口的超时时间。


http://www.ppmy.cn/server/116901.html

相关文章

怎样将vue项目 部署在ngixn的子目录下

如果同一服务器的80端口下,需要部署两个或以上数量的vue项目,那么就需要将其中一个vue项目部署在根目录下,其他的项目部署在子目录下. 像这样的配置 访问根目录 / 访问灭火器后台管理,访问 /mall/ 访问商城的后台管理 那么商场的vue项目,这样配置,才能在/mall/下正常访问? 1…

[数据集][目标检测]岩石种类检测数据集VOC+YOLO格式4766张9类别

数据集格式&#xff1a;Pascal VOC格式YOLO格式(不包含分割路径的txt文件&#xff0c;仅仅包含jpg图片以及对应的VOC格式xml文件和yolo格式txt文件) 图片数量(jpg文件个数)&#xff1a;4766 标注数量(xml文件个数)&#xff1a;4766 标注数量(txt文件个数)&#xff1a;4766 标注…

网络丢包是如何产生的

在网络通信过程中&#xff0c;数据以数据包的形式在发送方和接收方之间传输。理想状态下&#xff0c;所有数据包都应准确无误地到达目的地。然而&#xff0c;在实际的网络环境中&#xff0c;数据包可能会在传输过程中丢失&#xff0c;这种现象被称为网络丢包。网络丢包会导致数…

MySQL从C盘迁移到D盘

文章目录 前言一、停止MySQL服务打开服务&#xff08;方式一&#xff09;打开服务&#xff08;方式二&#xff09;停止MySQL服务 二、找到C盘中的文件文件夹1文件夹2文件夹3 三、修改文件内容1.对应文件夹12.对应文件夹3 四、 修改注册表中文件路径1.打开注册表2. 修改注册表中…

黑龙江等保测评二级系统费用解析:如何合理预算?

在信息安全日益受到重视的今天&#xff0c;等保测评成为企业合规的重要环节。尤其是在黑龙江&#xff0c;随着网络安全法的实施&#xff0c;越来越多的企业开始关注等保测评的相关费用。那么&#xff0c;黑龙江等保测评二级系统的费用是如何计算的呢&#xff1f; 首先&#xf…

基于SpringBoot+Vue的鲜花销售/鲜花商城/花店管理系统

作者&#xff1a;计算机学姐 开发技术&#xff1a;SpringBoot、SSM、Vue、MySQL、JSP、ElementUI、小程序等&#xff0c;“文末源码”。 专栏推荐&#xff1a;前后端分离项目源码、SpringBoot项目源码、SSM项目源码 系统展示 【2025最新】基于JavaSpringBootVueMySQL的鲜花销售…

Kafka原理剖析之「Topic创建」

一、前言 Kafka提供了高性能的读写&#xff0c;而这些读写操作均是操作在Topic上的&#xff0c;Topic的创建就尤为关键&#xff0c;其中涉及分区分配策略、状态流转等&#xff0c;而Topic的新建语句非常简单 bash kafka-topics.sh \ --bootstrap-server localhost:9092 \ // …

OZON电子产品大幅增长,OZON跨境PS5销量激增

Top1 存储卡 Карта памяти Canvas Select Plus 128 ГБ 商品id&#xff1a;1548303593 月销量&#xff1a;2131 欢迎各位卖家朋友点击这里&#xff1a; &#x1f449; D。DDqbt。COm/74rD 免费体验 随着智能手机和平板电脑的普及&#xff0c;用户对于存储空…

Apache SeaTunnel Committer 进阶指南

Apache SeaTunnel 作为一个开源的数据集成工具&#xff0c;旨在简化和加速海量数据的采集和传输。 社区的 Committer 是指拥有项目存储库的写权限的社区成员&#xff0c;即 Committer 可以自行修改代码、文档和网站&#xff0c;也可以合并其他成员的贡献。成为 Apache SeaTunn…

类组件化websocket的方法(心跳机制)

/*** WebSocket统一管理*/ export class WebSocketClient {constructor(url) {if (!url) {throw new Error("WebSocket URL is required.");}this.url url;this.websocket null;this.listeners {};this.heartbeatInterval 30000; // 心跳检测间隔&#xff08;毫秒…

动态获取git版本号

有时为了方便查用户使用版本情况&#xff0c;我们需要在某些接口加入git版本号。那问题来了&#xff0c;每次发版时都要手动修改版本号&#xff0c;既加大了工作量&#xff0c;又容易忘记。如果能动态注册版本号就方便多了。 接下来我们说下如何动态注入版本号。 // vue.confi…

决策树基础概论

1. 概述 在机器学习领域&#xff0c;决策树&#xff08;Decision Tree&#xff09; 是一种高度直观且广泛应用的算法。它通过一系列简单的是/否问题&#xff0c;将复杂的决策过程分解为一棵树状结构&#xff0c;使得分类或回归问题的解决过程直观明了。决策树的最大特点在于可…

Rust 所有权 借用与引用

文章目录 发现宝藏1. 所有权&#xff08;Ownership&#xff09;2. 引用&#xff08;References&#xff09;2.1 不可变引用2.2 可变引用2.3 引用的规则 3. 悬垂引用&#xff08;Dangling References&#xff09;4. 借用&#xff08;Borrowing&#xff09;结论 发现宝藏 前些天…

数据结构应用实例(五)——关键路径

Content: 一、问题描述二、算法思想三、代码实现四、小结 一、问题描述 设计实现 AOE 网的关键活动与关键路径问题&#xff1b; 二、算法思想 获取拓扑序列&#xff1b;计算节点的最早开始时间 v e [ i ] ve[i] ve[i]&#xff1b;计算节点的最晚开始时间 v l [ j ] vl[j] v…

如何用静态住宅代理实现分布式代理网络

分布式代理网络能够显著提升覆盖范围和稳定性&#xff0c;尤其在需要处理大量请求和确保高可用性的应用场景中。在现代网络架构中&#xff0c;静态住宅代理因其稳定性和真实IP地址的优势&#xff0c;成为了分布式代理网络设计的重要组成部分。本文将详细探讨如何设计和实现分布…

Day19_0.1基础学习MATLAB学习小技巧总结(19)——MATLAB绘图篇(2)

利用空闲时间把碎片化的MATLAB知识重新系统的学习一遍&#xff0c;为了在这个过程中加深印象&#xff0c;也为了能够有所足迹&#xff0c;我会把自己的学习总结发在专栏中&#xff0c;以便学习交流。 参考书目&#xff1a;《MATLAB基础教程 (第三版) (薛山)》 之前的章节都是…

计算机网络 ---如何寻找目标计算机

序言 在没有产生网络之前&#xff0c;单个主机就像汪洋大海之间的一个孤岛&#xff0c;和其他主机之间没有任何联系。但随着需求的产生&#xff0c;免不了两台主机之间需要产生交流&#xff08;传送数据等&#xff09;。离得近的两台主机之间搭一根网线就能够解决&#xff0c;但…

Matlab的4个取整函数

Round 舍入至最近的小数或整数 Y round(X) 将 X 的每个元素四舍五入为最近的整数。在舍入机会均等的情况下&#xff0c;即有元素的十进制小数部分为 0.5&#xff08;在舍入误差内&#xff09;时&#xff0c;round 函数会偏离零四舍五入到最接近的具有更大幅值的整数。 Y r…

什么是数字取证?

据联邦调查局称&#xff0c;2021 年发生了 80 多万起网络犯罪。由于其隐秘性&#xff0c;此类犯罪很容易发生。 当你坐在咖啡店里使用他们的 Wi-Fi 时&#xff0c;你怎么知道你不是同一网络上某人犯罪的受害者呢&#xff1f; 律师和检察官在数字取证专家的帮助下打击此类犯罪…

【重点】(非常全) Node.js的生态有哪些包

Node.js的生态系统非常丰富&#xff0c;包括了各种框架、库、工具等。以下是Node.js生态系统的一些主要组成部分&#xff1a; 1. 框架&#xff1a;如Express.js, Koa.js, Hapi.js, Sails.js, Nest.js等&#xff0c;用于快速构建Web应用或API服务。 2. 数据库驱动和ORM&#x…