如何实现具备定时缓存、到期自动重载功能的Mono?适配/jwks端点
解决Reactor缓存JWKS数据的问题
针对你在/jwks端点缓存数据库数据时遇到的问题,以下是具体分析和解决方案:
关于Mono.cache(Duration)的问题
你遇到的“仅执行一次调用,之后返回空值”,核心原因是若首次数据库查询返回的是空Mono(而非包含空集合的Mono),cache(Duration)会缓存这个空信号,直到过期前所有请求都会返回空。
解决方式:
- 确保数据库查询逻辑返回的是包含实际数据(或空集合)的
Mono,而非空Mono。比如查询结果为空时,返回Mono.just(Collections.emptyList())而不是Mono.empty()。 - 若需要针对空结果设置更短的缓存时长,使用
cache的重载方法:// 成功结果缓存10分钟,空结果仅缓存1分钟,更快触发重新查询 mono.cache(Duration.ofMinutes(10), Duration.ofMinutes(1));
关于Flux.cache(Duration)的问题
Flux.cache(Duration)默认会缓存所有元素,每次订阅都会重放全部元素。你遇到“仅返回最后一条条目”,大概率是订阅时误用了last()、takeLast(1)这类操作符,或者源Flux本身只发出最后一个元素。
解决方式:
- 如果需要返回所有JWK元素,直接订阅缓存后的
Flux即可,不要额外添加限制只取最后一个的操作符。 - 更推荐用
Mono<List<JWK>>包装整个JWKS集合,这更符合JWKS接口返回单个JSON数组的场景,缓存和序列化也更简单。
推荐的实现方案
以下是生产环境常用的缓存JWKS数据的代码示例:
import reactor.core.publisher.Mono; import java.time.Duration; import java.util.Collections; import java.util.List; import java.util.Map; // 数据库查询逻辑,每次调用都会执行实际查询 private Mono<List<JWK>> fetchJwksFromDb() { return Mono.fromCallable(() -> { // 替换为你的数据库查询代码 List<JWK> jwks = jwkRepository.findAll(); // 确保不会返回空Mono,空结果返回空集合 return jwks != null ? jwks : Collections.emptyList(); }).subscribeOn(Schedulers.boundedElastic()); // 数据库查询放在IO线程池 } // 缓存实例:全局单例,确保缓存共享 private final Mono<List<JWK>> cachedJwks = fetchJwksFromDb() .cache( Duration.ofMinutes(10), // 成功结果缓存10分钟 Duration.ofMinutes(1), // 空结果缓存1分钟(可选) Duration.ofMinutes(10) // 错误结果缓存10分钟(可选,根据错误类型调整) ); // /jwks端点处理方法 @GetMapping("/jwks") public Mono<Map<String, List<JWK>>> getJwks() { return cachedJwks.map(jwks -> Map.of("keys", jwks)); }
这个方案的优势:
- 首次请求触发数据库查询,后续请求直接返回缓存,直到10分钟后缓存过期自动刷新。
- 避免空
Mono导致的持续空返回问题。 - 数据库查询放在IO线程池,不阻塞主线程。
内容的提问来源于stack exchange,提问作者Archimedes Trajano
相关产品推荐
相关产品推荐

