在 Spring 中使用 Reactor Mono.cache()优化性能

简介: Memoization 是一种优化技术,用于通过存储昂贵的函数调用的结果并在再次出现相同的输入时重用缓存的结果来加速应用程序。在反应式编程的上下文中,记忆化通过缓存结果来帮助避免重复执行昂贵的操作。让我们深入研究一下如何将 Spring Reactor Mono 用作缓存。

Memoization 是一种优化技术,用于通过存储昂贵的函数调用的结果并在再次出现相同的输入时重用缓存的结果来加速应用程序。在反应式编程的上下文中,记忆化通过缓存结果来帮助避免重复执行昂贵的操作。让我们深入研究一下如何将 Spring Reactor Mono 用作缓存。

Reactor 中的 Mono.cache() 允许您缓存 Mono 的结果并将其重放给后续订阅者。这在处理网络调用或数据库查询等昂贵的操作时特别有用。

(Expensive operation) --Mono--> (cache) --Mono--> (result)

在此图中,第一个订阅者触发昂贵的操作,并且其结果被缓存。后续订阅者会收到缓存的结果,而不会再次触发昂贵的操作。

Mono.cache 优点
性能优化:缓存减少了对冗余计算或数据获取操作的需求,从而缩短了响应时间。缓存的 Mono 的后续订阅者可以重用缓存的结果,从而避免重复操作的开销。

资源优化:通过避免重复调用相同的数据来减少后端服务或数据库的负载。通过最大限度地减少请求数量来优化网络使用。

一致性:确保多个订阅者在缓存持续时间内获得一致的结果,因为它们都接收相同的缓存值。

可控性:允许对缓存持续时间进行精细控制,确保根据需要定期刷新数据。

Mono.cache 使用场景
昂贵的计算:在执行计算成本高昂的操作时,缓存结果可以显著减少后续请求的处理时间。比如:复杂数据转换、机器学习模型推理。

频繁的数据访问:当数据被多个使用者频繁访问但不经常更改时。比如:会话中的配置设置、用户配置文件数据。

API 速率限制:在处理具有速率限制的外部 API 时,缓存响应有助于保持在这些限制范围内。比如:API 调用数量受限的第三方服务集成。

静态数据:静态或不经常更改的数据可以从缓存中受益,以避免不必要的 fetch 操作。比如:国家/地区列表、网页的静态内容。

数据库查询结果:缓存在执行时间或资源使用方面成本高昂的数据库查询的结果。比如:聚合的分析数据、报告数据。

外部服务调用:当调用延迟可能较高的外部服务时,缓存响应可以提高应用程序的整体响应能力。比如:地理位置服务、货币兑换率。

欢迎关注 SpringForAll社区(spring4all.com),专注分享关于Spring的一切。

Mono.cache 代码案例
创建 Spring Boot应用,添加依赖,比如:

<parent>    <groupId>org.springframework.boot</groupId>    <artifactId>spring-boot-starter-parent</artifactId>    <version>2.7.1</version>    <relativePath /> <!-- lookup parent from repository --></parent> <properties>    <java.version>11</java.version></properties> <dependencies>    <dependency>        <groupId>org.springframework.boot</groupId>        <artifactId>spring-boot-starter-webflux</artifactId>    </dependency>    <dependency>        <groupId>org.springframework.boot</groupId>        <artifactId>spring-boot-starter</artifactId>    </dependency>    <dependency>        <groupId>org.springframework.boot</groupId>        <artifactId>spring-boot-starter-test</artifactId>        <scope>test</scope>    </dependency></dependencies>
  1. 创建DataService,包含 fetchData 方法的数据服务类,通过使用 delayElement(...) 方法将数据的发出延迟 2 秒来模拟成本高昂的操作。Mono.fromSupplier 方法用于创建在订阅时发出数据的 Mono。

@Servicepublic class DataService { public Mono fetchData() { return Mono.fromSupplier(() -> { System.out.println("Fetching data from service..."); return "Data from service"; }).delayElement(Duration.ofSeconds(2)); }}

  1. 创建Controller,实现以下接口

/data-no-cache:获取数据而不进行记忆化。每个请求都将触发 fetchData 方法,模拟 2 秒的延迟。
/data-cache:使用 Mono.cache() 通过记忆获取富贵论坛数据。第一个请求将触发 fetchData 方法,后续请求将返回缓存的结果。
/data-cache-duration:使用 Mono.cache(Duration.ofSeconds(10)) .第一个请求会触发 fetchData 方法,后续 10 秒内的请求会返回缓存的结果。10 秒后,缓存将过期,并将触发新的获取操作。
@RestControllerpublic class DataController { private final DataService dataService; private final Mono cachedData; private final Mono cachedDataWithDuration; public DataController(DataService dataService) { this.dataService = dataService; this.cachedData = dataService.fetchData().cache(); this.cachedDataWithDuration = dataService.fetchData().cache(Duration.ofSeconds(10)); } @GetMapping("/data-no-cache") public Mono getDataNoCache() { return dataService.fetchData(); } @GetMapping("/data-cache") public Mono getDataWithCache() { return cachedData; } @GetMapping("/data-cache-duration") public Mono getDataWithCacheDuration() { return cachedDataWithDuration; }}

  1. 创建应用主类

@SpringBootApplicationpublic class MemoizationExampleApplication { public static void main(String[] args) { SpringApplication.run(MemoizationExampleApplication.class, args); }}

  1. 测试

使用 mvn spring-boot:run 命令运行应用程序时,打开浏览器访问上面定义的接口。可以得到以下几个情况:

对 /data-no-cache 的每个请求都会触发 fetchData 方法,从而导致每个请求延迟 2 秒
对 /data-cache 的第一个请求会触发 fetchData 方法,后续请求会毫不延迟地返回缓存的结果。缓存日志在 Mono 中是隐式的。
对 /data-cache-duration 的第一个请求将触发 fetchData 方法。后续 10 秒内的请求将返回缓存结果。10 秒后,缓存将过期,并将触发新的获取操作。缓存日志在 Mono 中是隐式的。
总结
在 Reactor 中使用 Mono.cache() 是一种通过记住昂贵操作的结果来优化反应式应用程序的强大方法。这可确保成本高昂的计算或 I/O 操作仅执行一次,从而提高性能和效率。

相关文章
|
24天前
|
Dart 开发工具 Android开发
两个星期,用Flutter撸个APP
在编译Android版本的时候很顺畅,没有遇到任何问题。但是在编译iOS版本的时候,遇到了很多问题,直到现在也没有解决。
131 0
|
24天前
|
架构师 Java 数据库
Spring Boot技术路线图(从初级到架构师)
这个阶段成功的标志就是能够讲清楚技术实现方案,能够设计出高并发的稳定系统。
155 0
|
26天前
|
IDE Java Go
从 PHP 到 Java
一个月多来,感觉自己勉强入门了,虽然还有很多东西不熟悉,甚至都没有接触过,但不再是之前的一头雾水,面对小问题也一脸茫然了。所以我觉得也有必要做一个小小的总结,不仅有利于自己进步,最好也能帮其他 Java 新人少走一些弯路。
53 2
|
27天前
|
人工智能 Java 数据挖掘
Python 为什么是零基础学编程的最佳选择?
Python 的“全能性”让它覆盖了从日常工具到工业级项目的全场景,新手学习后能快速获得“成就感”:
158 3
|
29天前
|
安全 网络协议 网络安全
三分钟了解http和https
表现形式:HTTPS站点会在地址栏上显示一把绿色小锁,表明这是加密过的安全网站,如果采用了全球认证的顶级EV SSL证书的话,其地址栏会以绿色高亮显示,方便用户辨认。
203 1
|
23天前
|
人工智能 自然语言处理 监控
利用surging 网络组件重构插件开发
在大模型Agent生态中,插件是连接AI能力与外部工具的核心桥梁。通过标准化的插件开发,开发者可以快速扩展Agent的功能边界,实现从"文本交互"到"实际行动"的跨越。然而,传统插件架构往往因过度耦合而显得臃肿,难以适应复杂场景下的灵活扩展需求。
83 0
|
23天前
|
人工智能 自动驾驶 安全
本体论的启示:从零开始,如何让AI“学会”使用计算器
当AI只懂数字和加减,却不懂运算顺序时,我们如何让它理解计算器的运作逻辑?
100 0
|
24天前
|
缓存 Java Nacos
Spring Boot服务预热机制,杜绝大流量下新服务崩溃
梯度1(10%流量):新实例启动后,富贵论坛APP配置上述权重,观察新实例的CPU、内存、接口响应时间等指标,若指标稳定(CPU<70%,响应时间<200ms,错误率<0.1%),维持10-15分钟;
137 0
|
24天前
|
人工智能 Cloud Native 网络协议
极简外壳,异构内核:6G核心网协议栈的工程纠偏逻辑
从这个维度审视,引入增强型网络负载控制并利用AI能力进行故障预判,绝不仅是运营维护的锦上添花,而是防止整个AI驱动的基础设施陷入"重连死锁"的绝对物理生命线。
89 0
|
24天前
|
API C# 索引
C# 实现 PDF 页面拆分:单页、指定页精准拆分
安装:在 NuGet 包管理器里搜 FreeSpire.PDF 直接安装就行。这是一个免费社区版,唯一的限制是单次处理不能超过 10 页,如果超过 10 页,后面的页会被悄悄截掉(不会报错,但结果会少页)。所以如果你只是偶尔处理小文件,这个库很顺手。
79 0

热门文章

最新文章