如何基于Mono实现WebClient调用的相同输入响应缓存
缓存相同文件名的WebClient调用响应方案
针对相同文件名重复发起WebClient请求导致耗时过长的问题,我们可以通过缓存请求结果来避免重复调用,以下是两种可行的实现方式:
方式一:基于ConcurrentHashMap + Reactor Cache操作符
利用ConcurrentHashMap缓存每个文件名对应的Mono实例,结合Reactor的cache()操作符缓存请求结果,确保相同文件名复用同一请求结果:
import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; import reactor.core.publisher.Mono; // 定义缓存容器 private final ConcurrentMap<String, Mono<String>> extensionCache = new ConcurrentHashMap<>(); public Mono<String> getExtension(String fileName) { // 复用已有缓存的Mono,不存在则创建新请求并缓存 return extensionCache.computeIfAbsent(fileName, name -> webClient.get() .uri(extensionUrl + "?file=" + name) .retrieve() .bodyToMono(String[].class) .map(extensions -> { System.out.println("finished retrieving"); return extensions[0]; }) .cache() // 缓存该Mono的响应结果,后续订阅直接返回缓存值 ); }
方式二:基于Caffeine/Guava Cache实现灵活缓存
如果需要更精细化的缓存配置(如过期时间、容量限制),可以使用Caffeine或Guava Cache,结合Mono.defer()实现:
import com.github.benmanes.caffeine.cache.Caffeine; import com.github.benmanes.caffeine.cache.LoadingCache; import reactor.core.publisher.Mono; import java.util.concurrent.TimeUnit; // 配置缓存规则:10分钟过期、最多缓存1000条数据 private final LoadingCache<String, Mono<String>> extensionCache = Caffeine.newBuilder() .expireAfterWrite(10, TimeUnit.MINUTES) .maximumSize(1000) .build(name -> webClient.get() .uri(extensionUrl + "?file=" + name) .retrieve() .bodyToMono(String[].class) .map(extensions -> { System.out.println("finished retrieving"); return extensions[0]; }) ); public Mono<String> getExtension(String fileName) { return Mono.defer(() -> extensionCache.get(fileName)); }
注意事项
- 缓存Key的一致性:若系统对文件名大小写不敏感,需将统一转换为小写/大写后作为缓存Key,避免因大小写差异导致重复缓存。
- 错误处理:如果WebClient请求失败,缓存的将是错误信号。若需重试失败请求,可在WebClient调用链中添加
retry()操作符,或配置缓存的自动刷新策略。 - 过期策略:根据业务数据的更新频率设置合理的过期时间,防止缓存过期数据影响业务逻辑。
内容的提问来源于stack exchange,提问作者Jack Johnson
相关产品推荐
相关产品推荐

