弹性功能

从 7.0 版本开始,Spring Framework 核心包含了常见的弹性功能,特别是用于方法调用的 @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)
}
1 这个原始的 Mono 将被重试规范修饰。

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

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

@ConcurrencyLimit

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

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

这旨在保护目标资源免受过多线程同时访问,类似于线程池或连接池的池大小限制,当达到限制时会阻止访问。

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

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

这种限制在虚拟线程中特别有用,因为通常没有线程池限制。对于异步任务,这可以通过 SimpleAsyncTaskExecutor 进行约束。对于同步调用,此注解通过 ConcurrencyThrottleInterceptor 提供等效行为,该拦截器自 Spring Framework 1.0 以来可用于 AOP 框架的编程式使用。

@ConcurrencyLimit 还有一个 limitString 属性,提供属性占位符和 SpEL 支持,作为上面基于 int 的示例的替代方案。

启用弹性方法

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

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

编程式重试支持

@Retryable 提供声明式方法来为 ApplicationContext 中注册的 bean 中的方法指定重试语义不同,RetryTemplate 提供了编程式 API 来重试任意代码块。

具体来说,RetryTemplate 根据配置的 RetryPolicy 执行并可能重试 Retryable 操作。

var retryTemplate = new RetryTemplate(); (1)

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

默认情况下,可重试操作将在抛出任何异常时重试:在初次失败后最多重试 3 次(maxRetries = 3),每次尝试之间延迟 1 秒。

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

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

例如,如果 maxRetries 设置为 4,则可重试操作将至少被调用一次,最多 5 次。

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

retryTemplate.execute(
        () -> jmsClient.destination("notifications").send(...));
1 显式使用 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(...));
1 指定一个或多个要包含的异常类型。
2 指定一个或多个要排除的异常类型。

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

自定义谓词可以与 includesexcludes 结合使用;但是,自定义谓词将始终在 includesexcludes 应用之后应用。

以下示例演示如何配置一个 RetryPolicy,该策略具有 4 次重试尝试和带有少量抖动的指数退避策略。

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(...));

一个 RetryListener 可以注册到 RetryTemplate 中,以响应在关键重试阶段(重试尝试之前、重试尝试之后等)发布的事件,您可以通过 CompositeRetryListener 组合多个监听器。

尽管 RetryPolicy 的工厂方法和构建器 API 涵盖了大多数常见的配置场景,但您可以实现自定义的 RetryPolicy,以完全控制应触发重试的异常类型以及要使用的 BackOff 策略。请注意,您还可以通过 RetryPolicy.Builder 中的 backOff() 方法配置自定义的 BackOff 策略。

© . This site is unofficial and not affiliated with VMware.