caffeine本身不提供自动重试机制,加载失败时异常直接抛出;重试需业务层根据异常类型(如ioexception、sqlexception)主动实现,可采用retrytemplate、手动循环、refreshafterwrite后台刷新或fallback降级等策略。

Java中使用Caffeine做本地缓存时,加载失败本身不会自动重试——Caffeine的CacheLoader或AsyncCacheLoader在抛出异常后会直接将异常传播给调用方,缓存条目不会写入,也不会触发重试逻辑。所谓“通过异常链重试”,本质是**由业务层主动捕获异常、解析异常原因,并决定是否重试加载**,而非Caffeine内置机制。
明确加载失败的典型场景
Caffeine加载失败通常发生在自定义CacheLoader.load(K key)中,例如:
- 远程HTTP调用超时或返回5xx(如
IOException、WebClientException) - 数据库查询失败(如
SQLException、连接池耗尽) - 依赖服务不可用(如
ServiceUnavailableException) - 数据格式错误导致反序列化失败(如
JsonProcessingException)
注意:NullPointerException、IllegalArgumentException等编程错误一般不应重试,而应修复代码逻辑。
手动实现带异常分类的重试逻辑
推荐在CacheLoader外包装一层具备重试能力的加载器,避免把重试耦合进纯加载逻辑。示例使用RetryTemplate(Spring Retry)或手动循环:
// 使用 Spring Retry(需引入 spring-retry)
RetryTemplate retryTemplate = RetryTemplate.builder()
.maxAttempts(3)
.fixedBackoff(100) // 100ms 间隔
.retryOn(IOException.class, SQLException.class)
.failOn(IllegalArgumentException.class, NullPointerException.class)
.build();
CacheLoader<string user> loader = key -> retryTemplate.execute(
ctx -> fetchUserFromRemote(key) // 可能抛出 IOException/SQLException
);</string>
若不用Spring,可手写简洁重试:
CacheLoader<string user> loader = key -> {
int maxRetries = 3;
for (int i = 0; i
<h3>利用Caffeine的refreshAfterWrite实现“后台静默重试”</h3>
<p>如果允许缓存短暂过期后异步刷新(不阻塞读请求),可用<code>refreshAfterWrite</code> + 异步加载器,让失败刷新在后台重试,不影响主流程:</p>
<pre class="brush:java;toolbar:false;">AsyncLoadingCache<string user> cache = Caffeine.newBuilder()
.refreshAfterWrite(30, TimeUnit.SECONDS)
.buildAsync(key -> CompletableFuture.supplyAsync(() -> {
try {
return fetchUserFromRemote(key);
} catch (Exception e) {
// 记录日志,不抛出 —— 后续仍会按周期再试
log.warn("Refresh failed for key {}, will retry later", key, e);
throw new CompletionException(e); // 保持异常语义
}
}));
// 调用 get(key) 仍返回旧值(若有),后台尝试刷新</string>
注意:refreshAfterWrite仅在**有读请求触发时才尝试刷新**,无访问则不会执行;且刷新失败不会影响已有缓存值。
结合LoadingCache与fallback策略兜底
对强一致性要求不高时,可在加载失败时返回默认值或降级数据,避免传播异常:
LoadingCache<string user> cache = Caffeine.newBuilder()
.maximumSize(10_000)
.build(key -> {
try {
return fetchUserFromRemote(key);
} catch (IOException | SQLException e) {
log.warn("Load failed for {}, returning fallback", key, e);
return User.defaultUser(); // 或从DB本地表查缓存副本
}
});</string>
该方式不重试,但提升系统韧性;可与前述重试逻辑组合使用(如重试2次失败后返回fallback)。
核心要点:Caffeine不管理重试,重试决策必须由你控制——依据异常类型区分可恢复/不可恢复错误,选择同步重试、后台刷新或降级兜底。关键不是“怎么链式重试”,而是“哪些异常值得重试、重试几次、间隔多久、失败后如何兜底”。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











