Spring 核心弹性功能:@ConcurrencyLimit、@Retryable 和 RetryTemplate

工程 | Sam Brannen | 2025 年 9 月 9 日 | ...

这是通往 GA 之路系列博客文章的第一篇,重点介绍了 Spring 产品组合中将于今年 11 月发布的下一个主要版本中的主要功能。

今天,我们很荣幸地宣布 Spring Framework 7.0 中即将推出的新弹性功能:并发限制重试支持

并发限制

对于某些任务和资源,可能需要限制并发级别。并发限制有效地保护目标资源免受过多线程同时访问,类似于线程池或连接池达到限制时阻止访问的效果。这种限制对于通常没有线程池限制的虚拟线程特别有用。

对于异步任务,可以通过 Spring Framework 的 SimpleAsyncTaskExecutor 上的 concurrencyLimit 属性进行限制。对于同步调用,可以通过 Spring Framework 的 ConcurrencyThrottleInterceptor 上的 concurrencyLimit 属性进行限制,该拦截器自 Spring Framework 1.0 起就存在,用于 AOP 框架的编程使用。

使用 Spring Framework 7.0,为给定方法调用配置并发限制变得容易得多。只需使用 @ConcurrencyLimit 注解 Spring 管理组件中的方法,并使用 @EnableResilientMethods 注解 @Configuration 类即可启用自动限制。或者,可以将 @ConcurrencyLimit 声明在类型级别,以将其应用于给定类层次结构中的所有代理调用方法,并且可以通过在上下文中定义 ConcurrencyLimitBeanPostProcessor bean 来显式启用 @ConcurrencyLimit

以下示例将 sendNotification() 方法的并发限制设置为 10。

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

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

@ConcurrencyLimit(1) // 1 is the default, but specifying it makes it clearer
public void sendNotification() {
    this.jmsClient.destination("notifications").send(...);
}

重试支持

正如谚语所说:如果一开始不成功,请再试一次。

幸运的是,这有时也适用于软件应用程序中的错误:某些类别的错误通常可以成功重试。

历史上,Spring 社区一直依赖 Spring Retry 项目来提供各种形式的重试支持。然而,今年我们决定在 Spring Framework 本身的最低层级整合核心重试支持。这种支持自然受到了 Spring Retry 项目的启发,但我们将其完全重新设计为 spring-corespring-context 模块中一组最小的核心重试功能。

使用 @Retryable 进行声明式重试

对于声明式重试支持,您可以使用 @Retryable 注解 Spring 管理组件中的方法,并使用 @EnableResilientMethods 注解 @Configuration 类以启用自动重试支持。或者,可以将 @Retryable 声明在类型级别,以将其应用于给定类层次结构中的所有代理调用方法,并且可以通过在上下文中定义 RetryAnnotationBeanPostProcessor bean 来显式启用 @Retryable

默认情况下,方法调用将针对抛出的任何异常进行重试:初始失败后最多重试 3 次,每次尝试之间延迟 1 秒。

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

您可以选择通过 @Retryable 中的 includesexcludes 和隐式 value 属性来限制触发重试的异常类型,如下所示。

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

请注意,@Retryable(MessageDeliveryException.class)@Retryable(includes = {MessageDeliveryException.class}) 的快捷方式。

以下示例演示了如何配置 5 次重试尝试和带有少量抖动的指数回退策略。

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

Spring Framework 中核心重试支持的一些功能可能与您在 Spring Retry 中熟悉的功能有所不同。例如,Spring Retry 的 @Retryable 注解中 maxAttempts 属性的值适用于重试操作的初始调用以及重试尝试;而 Spring Framework 中的 maxAttempts 仅适用于实际的重试尝试。

最后但同样重要的是,Spring Framework 中的 @Retryable 也适用于具有响应式返回类型的响应式方法,自动使用 Reactor 的重试功能修饰管道。

@Retryable(maxAttempts = 5, delay = 100)
public Mono<Void> sendNotification() {
    return Mono.from(...); // This Mono will get decorated with a retry spec.
}

使用 RetryTemplate 进行编程式重试

@Retryable 为单个方法指定重试语义的声明式方法不同,RetryTemplate 提供了一个编程式 API,用于重试任意代码块。具体来说,RetryTemplate 根据配置的 RetryPolicy 执行并可能重试 Retryable 操作。

// Implicitly uses RetryPolicy.withDefaults()
var retryTemplate = new RetryTemplate();

retryTemplate.execute(() -> jmsClient.destination("notifications").send(...));

@Retryable 一样,默认情况下,Retryable 操作将针对抛出的任何异常进行重试:初始失败后最多重试 3 次,每次尝试之间延迟 1 秒。如果只需要自定义重试尝试次数,可以使用 RetryPolicy.withMaxAttempts() 工厂方法,如下所示。

var retryTemplate = new RetryTemplate(RetryPolicy.withMaxAttempts(5));

retryTemplate.execute(() -> jmsClient.destination("notifications").send(...));

如果需要缩小重试异常的类型,可以通过 includes()excludes()predicate() 构建器方法实现。

var retryPolicy = RetryPolicy.builder()
        .includes(MessageDeliveryException.class)
        // .excludes(...)
        // .predicate(...)
        .build();

var retryTemplate = new RetryTemplate(retryPolicy);

retryTemplate.execute(() -> jmsClient.destination("notifications").send(...));

@Retryable 一样,您还可以完全配置 RetryPolicy – 例如,最大重试次数和指数回退策略。

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

对 Spring 产品组合的影响

当您升级到即将发布的各种 Spring 产品组合项目的主要版本时,您会注意到一些项目已经从 Spring Retry 支持迁移到 Spring Framework 中新的核心重试支持。有关详细信息,请参阅以下项目的相关 GitHub 问题和拉取请求。

下一步

我们鼓励您在项目中尝试这些新功能并向我们提供反馈!

有关更多详细信息,请查阅参考手册的弹性功能部分以及 org.springframework.core.retryorg.springframework.resilience.annotation 包中的 Javadoc。

获取 Spring 新闻通讯

通过 Spring 新闻通讯保持联系

订阅

领先一步

VMware 提供培训和认证,助您加速进步。

了解更多

获得支持

Tanzu Spring 提供 OpenJDK™、Spring 和 Apache Tomcat® 的支持和二进制文件,只需一份简单的订阅。

了解更多

即将举行的活动

查看 Spring 社区所有即将举行的活动。

查看所有