Guava Cache 在第三方接口异常时的优雅降级处理

简介: Guava Cache 在第三方接口异常时的问题处理

一、背景

在实际开发中,我们经常使用缓存(如 Guava Cache)来存储从第三方接口获取的数据,以提高系统性能和响应速度。然而,第三方接口可能会出现故障或超时,导致缓存加载失败。如果直接使用 getUnchecked 方法,Guava Cache 会在 load 方法抛出异常时直接抛出 UncheckedExecutionException,这可能会导致系统中断或用户体验下降。对于一些非关键数据,我们希望即使第三方接口出错,系统也能继续运行,并且不缓存错误结果。


二、问题分析

1. Guava Cache 的默认行为

  • 当 CacheLoader.load 方法抛出异常时,Guava Cache 会包装异常并抛出 ExecutionException 或 UncheckedExecutionException。
  • 异常结果不会被缓存,但系统会中断当前操作。

2. 需求

  • 在第三方接口出错时,系统不应中断,而是返回一个默认值或降级结果。
  • 错误结果不应被缓存,以便下次请求可以重试加载。


三、解决方案

为了实现上述需求,我们可以通过以下两种方式对 Guava Cache 进行扩展:

1. 方案一:手动捕获异常 + 失效缓存键

在调用 getUnchecked 时,手动捕获异常并处理,同时使缓存键失效,确保下次请求可以重新加载数据。

实现步骤:
  • 使用 try-catch 捕获 UncheckedExecutionException
  • 在捕获异常后,调用 cache.invalidate(key) 使当前键失效。
  • 返回一个默认值或降级结果。
  • 代码示例:
public V getCachedValueSafely(K key) {
    try {
        return cache.getUnchecked(key);
    } catch (UncheckedExecutionException e) {
        // 记录日志
        logger.error("loading cache failed, key={}", key, e.getCause());
        // 使当前键失效,下次访问时重新加载
        cache.invalidate(key);
        // 返回默认值
        return defaultValue;
    }
}
优点:
  • 实现简单,逻辑清晰。
  • 精准控制异常处理和缓存失效。
缺点:
  • 需要在每个 get 调用处添加重复代码。

2. 方案二:自定义 CacheLoader + 空值包装

CacheLoader 内部捕获异常,并返回一个特殊标记(如 Optional.empty()),在外部调用时根据标记返回默认值。

实现步骤:
  • CacheLoader.load 方法中捕获异常,返回一个空值标记(如 Optional.empty())。
  • 在外部调用时,检查返回值是否为标记,如果是则返回默认值。
  • 代码示例:
LoadingCache<K, Optional<V>> cache = CacheBuilder.newBuilder()
    .build(new CacheLoader<K, Optional<V>>() {
        @Override
        public Optional<V> load(K key) {
            try {
                return Optional.of(thirdPartyAPI.loadData(key));
            } catch (Exception e) {
                // 返回空值标记
                return Optional.empty();
            }
        }
    });
public V getCachedValue(K key) {
    Optional<V> result = cache.getUnchecked(key);
    return result.isPresent() ? result.get() : defaultValue;
}
优点:
  • 逻辑集中,代码更简洁。
  • 避免在每个 get 调用处重复捕获异常。
缺点:
  • 需要处理 Optional 包装,可能对业务逻辑有一定侵入性。


四. 总结

在 Guava Cache 中处理第三方接口异常时,可以通过手动捕获异常 + 失效缓存键或自定义 CacheLoader + 空值包装的方式实现优雅降级。两种方案各有优劣,开发者可以根据具体场景选择合适的方式。对于非关键数据,这种设计能够有效提升系统的健壮性和用户体验,避免因第三方接口故障导致系统中断。


希望这个分析对大家有所帮助!另外延伸出另一个问题,如果这个第三方接口一直失败,你会如何解决这类缓存穿透的问题?欢迎一起讨论!


目录
相关文章
|
1月前
|
人工智能 自然语言处理 安全
Claude Code 全攻略:命令大全 + 实战工作流(建议收藏)
本文介绍了Claude Code终端AI助手的使用指南,主要内容包括:1)常用命令如版本查看、项目启动和更新;2)三种工作模式切换及界面说明;3)核心功能指令速查表,包含初始化、压缩对话、清除历史等操作;4)详细解析了/init、/help、/clear、/compact、/memory等关键命令的使用场景和语法。文章通过丰富的界面截图和场景示例,帮助开发者快速掌握如何通过命令行和交互界面高效使用Claude Code进行项目开发,特别强调了CLAUDE.md文件作为项目知识库的核心作用。
28128 65
Claude Code 全攻略:命令大全 + 实战工作流(建议收藏)
|
10月前
|
关系型数据库 MySQL Java
MySQL 分库分表 + 平滑扩容方案 (秒懂+史上最全)
MySQL 分库分表 + 平滑扩容方案 (秒懂+史上最全)
|
缓存 NoSQL Java
Guava Cache 异步刷新技巧,你值得拥有!
Guava Cache是一款非常优秀的本地缓存框架,提供非常简洁易用的 API 供开发者使用。 这篇文章,我们聊聊如何使用 Guava Cache **异步刷新技巧**带飞系统性能 。
Guava Cache 异步刷新技巧,你值得拥有!
|
数据处理 定位技术 开发者
甘特图、IPO图、DFD图
甘特图、IPO图、DFD图
|
消息中间件 Java Kafka
Spring Boot与Kafka的集成应用
Spring Boot与Kafka的集成应用
|
Java UED
基于SpringBoot自定义线程池实现多线程执行方法,以及多线程之间的协调和同步
这篇文章介绍了在SpringBoot项目中如何自定义线程池来实现多线程执行方法,并探讨了多线程之间的协调和同步问题,提供了相关的示例代码。
4949 0
|
SpringCloudAlibaba 监控 Java
SpringCloud Alibaba微服务-- Sentinel的使用(保姆级)
SpringCloud Alibaba微服务-- Sentinel的使用(保姆级)
|
算法 API 数据库
Zipline 3.0 中文文档(二)(3)
Zipline 3.0 中文文档(二)
410 1
|
运维 负载均衡 应用服务中间件
[nginx]反向代理grpc
[nginx]反向代理grpc
648 0