Spring 7 把重试做进了框架本身:@Retryable 默认怎么重试?退避怎么算?哪些异常会被重试?用完次数之后调用者看到什么?RetryTemplate 又是什么样?这篇在 Spring Framework 7.0.9 上用带时间戳的时间线逐项验证,也顺带验证了同包的 @ConcurrencyLimit。
--- 默认的 @Retryable(maxRetries = 3、delay = 1000 ms),前 2 次失败
5 ms defaults 第 1 次执行
1014 ms defaults 第 2 次执行
2019 ms defaults 第 3 次执行
2020 ms → 返回 ok@3
--- @Retryable(maxRetries = 4, delay = 100),前 2 次失败
0 ms fast 第 1 次执行
104 ms fast 第 2 次执行
206 ms fast 第 3 次执行
206 ms → 返回 ok@3@EnableResilientMethods 加 @Retryable,默认最多重试 3 次、间隔 1 秒。
--- maxRetries = 5, delay = 100, multiplier = 2, maxDelay = 500,总是失败
5 ms backoff 第 1 次执行
113 ms backoff 第 2 次执行
317 ms backoff 第 3 次执行
721 ms backoff 第 4 次执行
1226 ms backoff 第 5 次执行
1731 ms backoff 第 6 次执行
1732 ms → 抛 IllegalStateException: 失败 6delay、multiplier、maxDelay 控制间隔,maxDelay 封顶。
--- includes / excludes / 嵌套的原因 / predicate
4 ms onlyIo 执行(抛 IOException)
62 ms onlyIo 执行(抛 IOException)
118 ms onlyIo 执行(抛 IOException)
173 ms onlyIo 执行(抛 IOException)
174 ms → 抛 IOException: io
174 ms onlyIo 执行(抛 IllegalStateException)
174 ms → 抛 IllegalStateException: ise
175 ms excl 执行(抛 IllegalArgumentException)
175 ms → 抛 IllegalArgumentException: iae
175 ms excl 执行(抛 IllegalStateException)
230 ms excl 执行(抛 IllegalStateException)
285 ms excl 执行(抛 IllegalStateException)
340 ms excl 执行(抛 IllegalStateException)
340 ms → 抛 IllegalStateException: ise
341 ms nested 执行(抛 RuntimeException,cause 是 IOException)
394 ms nested 执行(抛 RuntimeException,cause 是 IOException)
446 ms nested 执行(抛 RuntimeException,cause 是 IOException)
446 ms → 抛 RuntimeException: 包了一层 ← 原因 IOException: 底层
448 ms pred 执行(消息 可重试:x)
503 ms pred 执行(消息 可重试:x)
556 ms pred 执行(消息 可重试:x)
612 ms pred 执行(消息 可重试:x)
612 ms → 抛 IllegalStateException: 可重试:x
613 ms pred 执行(消息 别重试)
613 ms → 抛 IllegalStateException: 别重试includes、excludes、predicate 决定哪些异常重试,嵌套的 cause 也参与匹配。
--- maxRetries = 2,总是失败:次数用完之后调用者看到什么
4 ms exhaust 执行
62 ms exhaust 执行
118 ms exhaust 执行
119 ms → 抛 IllegalStateException: 总是失败
--- timeout = 500 ms、delay = 200 ms、maxRetries = 10,总是失败
0 ms withTimeout 执行
204 ms withTimeout 执行
408 ms withTimeout 执行
408 ms → 抛 IllegalStateException: 总是失败次数用完抛原始异常,timeout 限制包括等待在内的总时间。
--- RetryTemplate(maxRetries = 2、delay = 50 ms),前 2 次失败
0 ms RetryTemplate 执行第 1 次
58 ms RetryListener.beforeRetry
58 ms RetryTemplate 执行第 2 次
58 ms RetryListener.onRetryFailure(编程式失败 2)
113 ms RetryListener.beforeRetry
113 ms RetryTemplate 执行第 3 次
114 ms RetryListener.onRetrySuccess
114 ms → 返回 ok
--- RetryTemplate 总是失败
0 ms RetryTemplate 执行(总是失败)
55 ms RetryListener.beforeRetry
55 ms RetryTemplate 执行(总是失败)
55 ms RetryListener.onRetryFailure(总是失败)
110 ms RetryListener.beforeRetry
110 ms RetryTemplate 执行(总是失败)
110 ms RetryListener.onRetryFailure(总是失败)
110 ms RetryListener.onRetryPolicyExhaustion
111 ms → 次数用完:RetryException: Retry policy for operation 'demo.Main$$Lambda' exhausted; aborting execution(最后的原因 IllegalStateException,suppressed 2 个)编程式重试,监听器只管“重试”,次数用完抛 RetryException。
@ConcurrencyLimit(2),8 个线程同时调用一个耗时 300 ms 的方法:成功 8 个,被拒绝 0 个,全部结束花了 1219 ms,方法内观察到的最大并发 2
@ConcurrencyLimit(2, policy = REJECT),8 个线程同时调用一个耗时 300 ms 的方法:成功 2 个,被拒绝 6 个(InvocationRejectedException),全部结束花了 305 ms限制同一个方法的并发数,多余的排队或拒绝。