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 社群所有即將舉行的活動。

檢視所有