📋 目录
一、工具类功能
把「调不通就再试几次」从业务代码里挪出来,固定间隔、指数退避、无返回值动作三种入口,成功值、实际尝试次数与最后一次异常都在结果里,重试范围可按异常类型收窄。
二、所需环境
JDK 17或以上
三、方法与调用示例(按需调用)
🔢 方法序号:1
🎯 方法用途:
- 按固定间隔重试一个有返回值的调用,成功值、尝试次数与最后一次异常都装进结果对象
- 下游偶发抖动、想把「再试几次」从业务代码里挪出来时用
🧩 方法定义:public static <T> RetryResult<T> fixedRetry(Supplier<T> supplier, int maxAttempts, long intervalMs, Predicate<Throwable> retryWhen)
🗣️ 入参解释:public static <T> RetryResult<T> fixedRetry(业务调用, 最大尝试次数, 间隔毫秒, 可重试谓词)
📤 返回值解释:RetryResult<T>,重试过程本身不向外抛业务异常
💡 边界口径:
- 默认只重试
Exception家族,Error不重试但会被记进lastFailure,不会被抛穿 - 谓词返回 false 时一次就停,参数写错这类确定性失败不会白等几轮
retryWhen传 null 就用默认谓词- 业务调用为 null、
maxAttempts非正、间隔为负都抛IllegalArgumentException - 等待期间线程被中断时恢复中断标记并抛
IllegalStateException,不吞中断信号
🧪 方法示例:
- 首次就成功时拿到
成功 true,尝试 1 次,业务调用 1 次,值 订单已创建 - 第 3 次才成功拿到
成功 true,尝试 3 次,值 3 - 全部失败拿到
成功 false,尝试 4 次,值为 null,最后一次异常 下游一直不可用,第4次 - 谓词判定不可重试时拿到
尝试 1 次,业务调用 1 次,一次就停 - 默认口径下抛
AssertionError拿到调用 1 次,lastFailure 类型 AssertionError,不重试但记录在案 - 谓词放开后同一个
Error拿到调用 3 次,重试次数跟着maxAttempts fixedRetry(() -> "x", 3, -1L, t -> true)抛间隔不能为负,当前为-1- 业务调用传 null 抛
业务调用不能为null
🔢 方法序号:2
🎯 方法用途:
- 指数退避重试:间隔按
initialDelayMs × 2^(n-1)翻倍,并被maxDelayMs封顶 - 下游需要喘息时间、或者担心重试把对方压垮时用这一版
🧩 方法定义:public static <T> RetryResult<T> exponentialRetry(Supplier<T> supplier, int maxAttempts, long initialDelayMs, long maxDelayMs, Predicate<Throwable> retryWhen)
🗣️ 入参解释:public static <T> RetryResult<T> exponentialRetry(业务调用, 最大尝试次数, 初始延迟, 最大延迟, 可重试谓词)
📤 返回值解释:RetryResult<T>,与固定间隔版同一套结果口径
💡 边界口径:
- 翻倍过程做饱和处理,极端次数下不会因为
long溢出变成负数而退化成不等待狂重试 - 未内置随机抖动,多实例同拍失败时节拍一致,高并发请自己叠加随机量
maxDelayMs小于initialDelayMs属参数错误,抛IllegalArgumentException- 中途恢复立即返回,不会把剩余次数跑完
🧪 方法示例:
- 退避 20/40/60/60 毫秒并被上限封顶,拿到
尝试 5 次,累计等待约 198 毫秒 - 中途恢复就提前返回,拿到
成功 true,尝试 3 次,不必跑完剩余次数 maxDelayMs给得比初始延迟小,抛最大延迟不能小于初始延迟,initialDelayMs=100,maxDelayMs=50
🔢 方法序号:3
🎯 方法用途:
- 重试一个没有返回值的动作,成功就返回实际尝试次数
- 同步推送、写缓存这类只关心成没成、失败要立刻知道的动作用它
🧩 方法定义:public static int runWithFixedRetry(Runnable runnable, int maxAttempts, long intervalMs, Predicate<Throwable> retryWhen)
🗣️ 入参解释:public static int runWithFixedRetry(无返回值动作, 最大尝试次数, 间隔毫秒, 可重试谓词)
📤 返回值解释:int,成功时的实际尝试次数
💡 边界口径:
- 与
fixedRetry的区别在失败处理:全部失败直接抛IllegalStateException,cause 是最后一次异常 - 想拿到失败结果而不是异常,用
fixedRetry - 动作传 null 也按
业务调用不能为null报,参数校验复用同一处 - 重试期间同样遵守中断即抛的口径
🧪 方法示例:
- 第 2 次成功时
runWithFixedRetry(runnable, 3, 20L, t -> true)拿到2 - 全部失败抛
重试2次后仍失败,cause 是同步接口持续 503 runWithFixedRetry(null, 3, 10L, t -> true)抛业务调用不能为nullrunWithFixedRetry(runnable, 0, 10L, t -> true)抛最大尝试次数必须大于0,当前为0
🔢 方法序号:4
🎯 方法用途:
- 手工装配一个重试结果,把已经拿到的值、次数与异常组成统一结构
- 自己实现了重试循环、但仍想复用同一套成功失败处理分支时用
🧩 方法定义:public record RetryResult<T>(T value, int attempts, Throwable lastFailure)
🗣️ 入参解释:public record RetryResult<T>(成功值, 实际尝试次数, 最后一次异常)
📤 返回值解释:RetryResult<T>,不可变的结果记录
💡 边界口径:
- 紧凑构造器把「至少尝试过一次」钉在构造点,
attempts小于 1 直接抛异常 value允许是 null,lastFailure也允许是 null,两者不构成矛盾- 成功时约定
lastFailure传 null,判定只看这个字段 - 是 record,可以直接
equals比较两份结果
🧪 方法示例:
new RetryResult<>(null, 2, lastError)走统一处理分支拿到成功 false,尝试 2 次new RetryResult<>(null, 0, lastError)抛尝试次数至少为1,当前为0
🔢 方法序号:5
🎯 方法用途:
- 判断这次重试到底成没成,依据是有没有记录到异常
- 拿到
RetryResult后的第一句就该是它,漏掉会把故障静默吞掉
🧩 方法定义:public boolean isSuccess()
🗣️ 入参解释:public boolean isSuccess()(无入参)
📤 返回值解释:boolean,lastFailure 为 null 时返回 true
💡 边界口径:
- 业务返回 null 不等于失败,这种调用算成功
- 全部失败时
value是 null 且lastFailure非 null,只看value会误判成成功 - 判定不看尝试次数,一次成的与三次成的都是成功
🧪 方法示例:
- 业务正常返回 null 时
isSuccess()拿到true,value是null - 全部失败的那次
isSuccess()拿到false,lastFailure留着最后一句异常
四、工具类源码(可直接复制)
📦 源码文件