跳到主要内容
版本:7.0.3

韧性特性

Hunyuan 7b 中英对照 Resilience Features

从7.0版本开始,核心Spring框架包含了常见的弹性特性,特别是用于方法调用的@Retryable@ConcurrencyLimit注解,以及编程式的重试支持

@Retryable

@Retryable 是一个注解,用于指定单个方法的重试特性(在方法级别声明该注解),或者指定给定类层次结构中所有通过代理调用的方法的重试特性(在类型级别声明该注解)。

@Retryable
public void sendNotification() {
this.jmsClient.destination("notifications").send(...);
}

默认情况下,如果抛出任何异常,方法调用将会被重试:在初次失败后,最多会进行3次重试(maxRetries = 3),并且每次重试之间会有1秒钟的延迟。

备注

一个被标记为@Retryable的方法将至少被调用一次,并且最多会被重试maxRetries次,其中maxRetries是允许的最大重试次数。具体来说,总尝试次数 = 1次初始尝试 + maxRetries次重试

例如,如果maxRetries被设置为4,那么这个@Retryable方法将至少被调用一次,并且最多会被重试5次。

如果需要,这可以针对每种方法进行具体调整——例如,通过 includesexcludes 属性来限定需要重试的异常类型。所提供的异常类型会与调用失败时抛出的异常以及嵌套异常进行匹配。

@Retryable(MessageDeliveryException.class)
public void sendNotification() {
this.jmsClient.destination("notifications").send(...);
}
备注

@Retryable(MessageDeliveryException.class)@Retryable(includes = MessageDeliveryException.class) 的缩写。

提示

对于高级用例,您可以通过@Retryable中的predicate属性指定一个自定义的MethodRetryPredicate,该谓词将用于根据Method和给定的Throwable来决定是否重试失败的方法调用——例如,通过检查Throwable的消息。

自定义谓词可以与includesexcludes结合使用;但是,自定义谓词总是会在includesexcludes应用之后才被执行。

或者尝试4次重试,并采用带有轻微抖动的指数退避策略:

@Retryable(
includes = MessageDeliveryException.class,
maxRetries = 4,
delay = 100,
jitter = 10,
multiplier = 2,
maxDelay = 1000)
public void sendNotification() {
this.jmsClient.destination("notifications").send(...);
}

最后但同样重要的是,@Retryable 也适用于具有反应式返回类型的反应式方法,它可以通过 Reactor 的重试功能来装饰处理流程:

@Retryable(maxRetries = 4, delay = 100)
public Mono<Void> sendNotification() {
return Mono.from(...); 1
}
  • 这个原始的 Mono 将会被装饰上重试规范(retry spec)。

有关各种特性的详细信息,请参阅@Retryable中提供的注解属性。

提示

@Retryable 中的几个属性有 String 类型变体,这些变体提供了属性占位符和 SpEL(Spel Expression Language)支持,作为上述示例中使用的特定类型注解属性的替代方案。

@ConcurrencyLimit

@ConcurrencyLimit 是一个注解,用于指定单个方法的并发限制(在方法级别声明该注解),或指定给定类层次结构中所有通过代理调用的方法的并发限制(在类型级别声明该注解)。

@ConcurrencyLimit(10)
public void sendNotification() {
this.jmsClient.destination("notifications").send(...);
}

这是为了保护目标资源不被过多的线程同时访问,其效果类似于线程池或连接池的池大小限制:当达到限制时,会阻止进一步的访问。

您可以选择将限制值设置为 1,从而有效地锁定对目标 Bean 实例的访问:

@ConcurrencyLimit(1)
public void sendNotification() {
this.jmsClient.destination("notifications").send(...);
}

这种限制在虚拟线程(Virtual Threads)中尤为有用,因为通常情况下虚拟线程并没有线程池的限制。对于异步任务,这种限制可以通过SimpleAsyncTaskExecutor来实现。而对于同步调用,这个注解可以通过ConcurrencyThrottleInterceptor来实现类似的效果,该拦截器自Spring Framework 1.0起就已提供,可用于AOP框架的编程式控制。

提示

@ConcurrencyLimit 还有一个 limitString 属性,它提供了属性占位符和 SpEL 支持,作为上述基于 int 的示例的另一种选择。

启用弹性方法

与Spring的许多基于注解的核心特性一样,@Retryable@ConcurrencyLimit被设计为元数据,你可以选择遵守或忽略它们。启用这些弹性处理注解的最便捷方法是在相应的@Configuration类上声明@EnableResilientMethods

或者,也可以通过在上下文中定义一个RetryAnnotationBeanPostProcessorConcurrencyLimitBeanPostProcessorbean来单独启用这些注解。

程序化重试支持

@Retryable不同,后者为在ApplicationContext中注册的bean中的方法指定重试语义提供了一种声明式方法,而RetryTemplate则提供了一个编程式API,用于重试任意代码块。

具体来说,RetryTemplate 会根据配置好的 RetryPolicy 来执行并可能重新尝试一个 Retryable 操作。

var retryTemplate = new RetryTemplate(); 1

retryTemplate.execute(
() -> jmsClient.destination("notifications").send(...));
  • 隐式使用 RetryPolicy.withDefaults()

默认情况下,对于任何抛出的异常,可重试的操作将会被重新尝试:在初次失败后,最多会有3次重试机会(maxRetries = 3),并且每次重试之间会有1秒的延迟。

如果您只需要自定义重试次数,可以使用RetryPolicy.withMaxRetries()工厂方法,如下所示。

备注

可重试的操作将至少执行一次,并最多重试 maxRetries 次,其中 maxRetries 是最大重试次数。具体来说,total attempts = 1 次初始尝试 + maxRetries 次重试

例如,如果将 maxRetries 设置为 4,则该可重试操作将至少执行一次,并最多执行 5 次。

var retryTemplate = new RetryTemplate(RetryPolicy.withMaxRetries(4)); 1

retryTemplate.execute(
() -> jmsClient.destination("notifications").send(...));
  • 明确使用 RetryPolicy.withMaxRetries(4)

如果你需要缩小需要重试的异常类型范围,可以通过includes()excludes()构建器方法来实现。所提供的异常类型将会与失败操作抛出的异常以及嵌套原因中的异常进行匹配。

var retryPolicy = RetryPolicy.builder()
.includes(MessageDeliveryException.class) 1
.excludes(...) 2
.build();

var retryTemplate = new RetryTemplate(retryPolicy);

retryTemplate.execute(
() -> jmsClient.destination("notifications").send(...));
  • 指定一个或多个要包含的异常类型。

  • 指定一个或多个要排除的异常类型。

提示

对于高级用例,您可以通过 RetryPolicy.Builder 中的 predicate() 方法指定自定义的 Predicate<Throwable>,该谓词将用于根据给定的 Throwable 来决定是否重试失败的操作——例如,通过检查 Throwable 的消息。

自定义谓词可以与 includesexcludes 结合使用;但是,自定义谓词总是在 includesexcludes 被应用之后才被执行。

以下示例演示了如何配置一个具有4次重试尝试和指数退避策略的RetryPolicy,并在其中加入一些随机抖动(jitter)机制。

var retryPolicy = RetryPolicy.builder()
.includes(MessageDeliveryException.class)
.maxRetries(4)
.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(...));
提示

可以注册一个RetryListenerRetryTemplate中,以便在关键的重试阶段(如重试尝试之前、重试尝试之后等)对发生的事件做出响应;此外,还可以通过CompositeRetryListener组合多个监听器。

尽管RetryPolicy的工厂方法和构建器API涵盖了大多数常见的配置场景,但你仍然可以实现自定义的RetryPolicy,以便完全控制哪些类型的异常应该触发重试,以及使用哪种退避策略(BackOff)。需要注意的是,你也可以通过RetryPolicy.Builder中的backOff()方法来配置自定义的BackOff策略。