StopStrategies.stopAfterAttempt(3) 最安全:明确限制最大尝试次数,适合大多数 HTTP 调用或 DB 查询场景;禁用 neverStop(),避免无限重试导致线程池耗尽。

RetryingBuilder 的 stopStrategy 怎么选才不卡死
Guava Retry 的重试终止逻辑全靠 stopStrategy 控制,选错会导致无限重试或根本没重试就放弃。常见错误是直接用 StopStrategies.neverStop() 调试完忘了改,上线后某个异常持续触发,线程池被占满。
-
StopStrategies.stopAfterAttempt(3)最安全:明确限制最大尝试次数,适合大多数 HTTP 调用或 DB 查询场景 -
StopStrategies.stopAfterDelay(30, TimeUnit.SECONDS)适合耗时波动大的操作(比如文件上传),但要注意系统时钟漂移可能影响精度 - 别用
neverStop(),除非你同时配了retryIfExceptionOfType(TimeoutException.class)并确保该异常必然发生——现实中几乎做不到
retryIfException 和 retryIfResult 的区别到底在哪
这两个方法决定“什么情况下值得再试一次”,但语义完全不同:前者看异常类型,后者看返回值内容。混用或漏配会导致该重试的不重试、不该重试的狂重试。
-
retryIfExceptionOfType(IOException.class):只对网络类异常重试,NullPointerException直接抛出 -
retryIfException(predicate):可以写更细的判断,比如e.getMessage().contains("Connection refused"),但注意不要在 predicate 里做 IO 或锁操作 -
retryIfResult(Objects::isNull):适用于返回null表示临时不可用的接口(如缓存穿透兜底),但若方法本身允许合法返回null,这里就会误判
waitStrategy 配置不当会拖垮下游服务
等待策略不是越“聪明”越好。固定延迟最可控,指数退避容易在高并发下形成请求尖峰,而随机策略对调试不友好。
-
WaitStrategies.fixedWait(100, TimeUnit.MILLISECONDS):适合已知下游恢复时间稳定的场景(如依赖服务重启后 200ms 内可恢复) -
WaitStrategies.exponentialWait():默认从 100ms 开始翻倍,第 5 次重试就要等 1.6s,如果上游超时设的是 2s,大概率还没等到第 5 次就超时了 -
WaitStrategies.randomWait(50, TimeUnit.MILLISECONDS, 200, TimeUnit.MILLISECONDS):能缓解雪崩,但日志里看不出重试间隔规律,排查时得靠时间戳差值反推
Retryer 实例能不能共享
可以,而且应该复用。每次 new 一个 Retryer 都会创建独立的定时器和线程资源,频繁创建可能触发 java.lang.OutOfMemoryError: unable to create new native thread。
立即学习“Java免费学习笔记(深入)”;
- 把
Retryer声明为static final成员变量,或者通过 DI 容器单例管理 - 注意
Retryer不是线程安全的?错,Guava 的Retryer是线程安全的,多个线程调用call()没问题 - 唯一不能共享的是
Retryer.call()的回调逻辑——比如你在 lambda 里引用了局部变量,那就得确保那个变量本身是线程安全的
retryIfException 和 retryIfResult 的组合效果:它们是“或”关系,不是“且”。只要满足任一条件就重试,这点在混合使用 null 返回 + 特定异常时特别容易踩坑。










