弹性特性
从 7.0 版本开始,核心 Spring Framework 包含了常见的弹性特性,特别是用于方法调用的
@Retryable 和 @ConcurrencyLimit
注解,以及 resilience-programmatic-retry。
@Retryable
@Retryable 是一个注解,
用于指定单个方法(注解声明在方法级别)或给定类层次结构中所有代理调用的方法(注解声明在类型级别)的重试特性。
@Retryable
public void sendNotification() {
this.jmsClient.destination("notifications").send(...);
}
默认情况下,方法调用将在抛出任何异常时重试:在初次失败后最多重试 3 次,每次尝试之间延迟 1 秒。
如果需要,可以针对每个方法进行具体调整——例如,通过缩小重试的异常范围:
@Retryable(MessageDeliveryException.class)
public void sendNotification() {
this.jmsClient.destination("notifications").send(...);
}
或者进行 5 次重试,并采用指数退避策略并带有一些抖动:
@Retryable(
includes = MessageDeliveryException.class,
maxAttempts = 5,
delay = 100,
jitter = 10,
multiplier = 2,
maxDelay = 1000)
public void sendNotification() {
this.jmsClient.destination("notifications").send(...);
}
最后但同样重要的是,@Retryable 也适用于具有响应式返回类型的响应式方法,它使用 Reactor 的重试功能装饰管道:
@Retryable(maxAttempts = 5, delay = 100)
public Mono<Void> sendNotification() {
return Mono.from(...); [id="CO1-1"][id="CO1-1"](1)
}
<1> 这个原始的 `Mono` 将被重试规范装饰。
有关各种特性的详细信息,请参阅 @Retryable 中可用的注解属性。
|
对于一些属性,也提供了带有占位符支持的 |
@ConcurrencyLimit
@ConcurrencyLimit
是一个注解,用于指定单个方法(注解声明在方法级别)或给定类层次结构中所有代理调用的方法(注解声明在类型级别)的并发限制。
@ConcurrencyLimit(10)
public void sendNotification() {
this.jmsClient.destination("notifications").send(...);
}
这旨在保护目标资源免受过多线程同时访问,类似于线程池或连接池的池大小限制,当达到限制时会阻止访问。
您可以选择将限制设置为 1,从而有效地锁定对目标 bean 实例的访问:
@ConcurrencyLimit(1) [id="CO2-1"][id="CO2-1"](1)
public void sendNotification() {
this.jmsClient.destination("notifications").send(...);
}
<1> 1 是默认值,但指定它使意图更清晰。
这种限制对于通常没有线程池限制的虚拟线程特别有用。对于异步任务,这可以在
SimpleAsyncTaskExecutor
上进行限制。对于同步调用,此注解通过
ConcurrencyThrottleInterceptor
提供等效行为,该拦截器自 Spring Framework 1.0 起就可用于 AOP 框架的编程式使用。
启用弹性方法
与 Spring 的许多核心基于注解的特性一样,@Retryable 和 @ConcurrencyLimit 被设计为元数据,您可以选择遵循或忽略。
启用弹性注解处理最便捷的方式是在相应的 @Configuration 类上声明
@EnableResilientMethods。
或者,可以通过在上下文中定义 RetryAnnotationBeanPostProcessor 或 ConcurrencyLimitBeanPostProcessor bean 来单独启用这些注解。
编程式重试支持
与 @Retryable 提供声明式方法来为 ApplicationContext 中注册的 bean 中的方法指定重试语义不同,
RetryTemplate
提供了一个编程式 API,用于重试任意代码块。
具体来说,RetryTemplate 根据配置的
RetryPolicy
执行并可能重试一个 Retryable 操作。
var retryTemplate = new RetryTemplate(); [id="CO3-1"][id="CO3-1"](1)
retryTemplate.execute(
() -> jmsClient.destination("notifications").send(...));
<1> 隐式使用 `RetryPolicy.withDefaults()`。
默认情况下,可重试操作将在抛出任何异常时重试:在初次失败后最多重试 3 次,每次尝试之间延迟 1 秒。
如果您只需要自定义重试尝试次数,可以使用 RetryPolicy.withMaxAttempts() 工厂方法,如下所示。
var retryTemplate = new RetryTemplate(RetryPolicy.withMaxAttempts(5)); [id="CO4-1"][id="CO4-1"](1)
retryTemplate.execute(
() -> jmsClient.destination("notifications").send(...));
<1> 显式使用 `RetryPolicy.withMaxAttempts(5)`。
如果您需要缩小重试异常的类型,可以通过 includes() 和 excludes() 构建器方法实现。
var retryPolicy = RetryPolicy.builder()
.includes(MessageDeliveryException.class) [id="CO5-1"][id="CO5-1"](1)
.excludes(...) [id="CO5-2"][id="CO5-2"](2)
.build();
var retryTemplate = new RetryTemplate(retryPolicy);
retryTemplate.execute(
() -> jmsClient.destination("notifications").send(...));
<1> 指定一个或多个要包含的异常类型。 <1> 指定一个或多个要排除的异常类型。
|
对于高级用例,您可以通过 |
以下示例演示如何配置一个 RetryPolicy,其中包含 5 次重试尝试,并采用指数退避策略并带有一些抖动。
var retryPolicy = RetryPolicy.builder()
.includes(MessageDeliveryException.class)
.maxAttempts(5)
.delay(Duration.ofMillis(100))
.jitter(Duration.ofMillis(10))
.multiplier(2)
.maxDelay(Duration.ofSeconds(1))
.build();
var retryTemplate = new RetryTemplate(retryPolicy);
retryTemplate.execute(
() -> jmsClient.destination("notifications").send(...));
|
|
尽管 RetryPolicy 的工厂方法和构建器 API 涵盖了大多数常见的配置场景,
但您可以实现自定义的 RetryPolicy,以完全控制应触发重试的异常类型以及要使用的
BackOff 策略。
请注意,您还可以通过 RetryPolicy.Builder 中的 backOff() 方法配置自定义的 BackOff 策略。