知識庫 / Spring RSS 訂閱

Spring Retry 指南

Spring
HongKong
4
02:40 PM · Dec 06 ,2025

1. 概述

Spring Retry 提供自動重試失敗操作的能力。 這在錯誤可能是暫時的(例如短暫的網絡故障)時非常有用。 但是,請注意,重試與斷路器不同。

在本教程中,我們將看到使用 Spring Retry 的各種方法:註解、RetryTemplate 和回調。

2. Maven 依賴

讓我們首先將 spring-retry 依賴添加到我們的 pom.xml 文件中:

<dependency>
    <groupId>org.springframework.retry</groupId>
    <artifactId>spring-retry</artifactId>
    <version>2.0.3</version>
</dependency>

我們還需要在項目中添加 spring-boot-starter-aspectj 依賴:

<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-aspects</artifactId>
    <version>6.1.5</version>
</dependency>

此外,spring-aspects 依賴項至關重要,因為它會自動配置 Spring Boot 中的面向切面編程 (AOP)。請訪問 Maven Central 以獲取 spring-retryspring-aspects 依賴項的最新版本。

3. 啓用 Spring Retry

為了在應用程序中啓用 Spring Retry,我們需要將 @EnableRetry 註解添加到我們的 @Configuration 類中:

@Configuration
@EnableRetry
public class AppConfig { ... }

4. 使用 Spring Retry

Spring Retry 是一個用於簡化重試邏輯的庫。它允許你輕鬆地重試失敗的請求,而無需編寫複雜的重試策略。Spring Retry 提供了多種重試模式,包括基於時間的重試、基於異常的重試和基於條件的重試。

核心概念

  • RetryTemplate: RetryTemplate 是 Spring Retry 的核心類,它封裝了重試邏輯,並允許你配置重試行為。
  • RetryTemplateBuilder: RetryTemplateBuilder 允許你配置 RetryTemplate 的各種屬性,例如重試次數、延遲時間、最大延遲時間等。
  • RetryCallback: RetryCallback 是一個接口,它定義了重試邏輯。你必須實現這個接口,並提供重試執行的代碼。
  • RetryEvent: RetryEvent 是一個事件,它記錄了重試的詳細信息,例如重試次數、延遲時間、異常類型等。

配置 RetryTemplate

你可以使用 RetryTemplateBuilder 配置 RetryTemplate 的各種屬性。以下是一些常用的屬性:

  • retryable: 如果該屬性設置為 true,則在重試後繼續執行方法。如果設置為 false,則在重試後拋出異常。
  • backoffExponent: 控制重試之間的延遲時間。默認值為 1.0。
  • maxAttempts: 控制重試的最大次數。
  • delay: 控制重試之間的延遲時間,單位為毫秒。

示例代碼

@Component
public class MyService {

    @Retryable(
            whatException = {IOException.class, RuntimeException.class},
            backoff = {BackoffStrategy.fixed(500, 3), BackoffStrategy.exponential(3, 3)}
    )
    public void doSomething() throws IOException {
        // ... 你的代碼 ...
    }
}

在這個例子中,doSomething() 方法在發生 IOExceptionRuntimeException 時,會嘗試重試三次。第一次重試的延遲為 500 毫秒,第二次重試的延遲為 500 * 2 毫秒,第三次重試的延遲為 500 * 4 毫秒。

4.1. 不帶恢復的 @Retryable 註解

我們可以使用 @Retryable 註解為方法添加重試功能:

@Service
public interface MyService { 

    @Retryable 
    void retryService(); 
}

由於我們未指定任何異常情況,因此將嘗試針對所有異常進行重試。根據 @Retryable 的默認行為,重試可能會最多發生三次,每次重試之間間隔一秒。此外,最大嘗試次數為 3,包括初始失敗調用和兩個後續重試。如果達到最大嘗試次數且仍存在異常,則會拋出 ExhaustedRetryException

4.2. @Retryable@Recover

現在,我們將使用 @Recover 註解添加一個恢復方法:

@Service
public interface MyService { 

    @Retryable(retryFor = SQLException.class)
    void retryServiceWithRecovery(String sql) throws SQLException; 

    @Recover
    void recover(SQLException e, String sql); 
}

在這裏,當拋出 SQLException 時,會嘗試重試。註解 @Recover 定義了一個單獨的恢復方法,當帶有 @Retryable 註解的方法由於指定異常而失敗時生效。

因此,如果 retryServiceWithRecovery 方法在三次重試後仍然拋出 SqlException 異常,則將調用 recover() 方法。

恢復處理程序應具有第一個參數為 Throwable 類型(可選)和相同的返回類型。其餘參數將按照失敗方法的參數列表順序填充。

4.3. 自定義 @Retryable 的行為

為了自定義重試行為,我們可以使用參數 <em >maxAttempts</em ><em >backoff</em >>

@Service
public interface MyService {

    @Retryable(retryFor = SQLException.class, maxAttempts = 2, backoff = @Backoff(delay = 100))
    void retryServiceWithCustomization(String sql) throws SQLException;
}

在上述示例中,會有最多兩次嘗試,並在嘗試之間間隔 100 毫秒。

4.4. 使用 Spring 屬性

我們可以使用屬性在 <em @Retryable</em> 註解中。

為了演示這一點,我們將如何將 <em delay</em><i>maxAttempts</em> 的值外部化到屬性文件中。

首先,讓我們在名為 <em retryConfig</em> 的文件中定義屬性:

retry.maxAttempts=2
retry.maxDelay=100

我們隨後指示我們的 @Configuration 類加載此文件:

// ...
@PropertySource("classpath:retryConfig.properties")
public class AppConfig { ... }

最後,我們可以將 retry.maxAttemptsretry.maxDelay 的值注入到我們的 @Retryable 定義中。

@Service 
public interface MyService {

    @Retryable(retryFor = SQLException.class, maxAttemptsExpression = "${retry.maxAttempts}",
      backoff = @Backoff(delayExpression = "${retry.maxDelay}")) 
    void retryServiceWithExternalConfiguration(String sql) throws SQLException; 
}

請注意,我們現在使用 maxAttemptsExpressiondelayExpression 代替 maxAttemptsdelay

4.5. 打印重試計數

為了在 Spring 中使用 @Retryable 註解時記錄重試計數,可以在方法中利用 RetrySynchronizationManager.getContext().getRetryCount() 來打印當前重試嘗試次數:

@Override
public void retryService() {
    logger.info("Retry Number: "+ RetrySynchronizationManager.getContext().getRetryCount());
    logger.info("throw RuntimeException in method retryService()");
    throw new RuntimeException();
}

在上述邏輯中,我們添加了一條關於重試次數的日誌信息。如果執行 retryService(),我們將獲取每個重試的日誌,以及其重試計數:

Retry Number: 0 
throw RuntimeException in method retryService() 
Retry Number: 1 
throw RuntimeException in method retryService() 
Retry Number: 2 
throw RuntimeException in method retryService()

正如日誌中所示,每次重試都會打印重試次數。

5. RetryTemplate

The RetryTemplate is a bean that allows you to retry an operation if it fails. It provides a convenient way to handle transient failures, such as network glitches or temporary service unavailability.

Key Features:

  • Automatic Retries: The RetryTemplate automatically retries the operation a specified number of times.
  • Customizable Retry Strategy: You can configure the retry strategy, including the number of retries, delay between retries, and the conditions under which retries should be attempted.
  • Support for Different Types of Operations: The RetryTemplate can be used with various types of operations, including method calls, HTTP requests, and database transactions.

Configuration:

You can configure the RetryTemplate using the following properties:

  • backoffInterval: The initial delay between retries in milliseconds.
  • maxAttempts: The maximum number of retries.
  • backoffPolicy: The policy used to determine the delay between retries. Common backoff policies include fixed delay, exponential backoff, and jittered backoff.

Example:

@Retryable(with = {Exception.class})
public String doSomethingRisky() {
    // ... some risky operation ...
    if (/* operation failed */) {
        throw new RuntimeException("Operation failed");
    }
    return "Operation successful";
}

Note: The RetryTemplate is often used in conjunction with the @Retryable annotation to simplify the implementation of retry logic.

5.1. 重試操作 (RetryOperations)

Spring Retry 提供 RetryOperations 接口,該接口提供了一組 execute() 方法。

public interface RetryOperations {

    <T> T execute(RetryCallback<T, ? extends Throwable> retryCallback) throws Exception;

    ...
}

RetryCallback,是 execute() 的一個參數,它是一個接口,允許在失敗時插入需要重試的業務邏輯:

public interface RetryCallback<T, E extends Throwable> {

    T doWithRetry(RetryContext context) throws Throwable;
}

5.2. 介紹 RetryPolicy 建造器 API 和工廠方法

RetryTemplateRetryOperations 的實現。

Spring Retry 通過引入工廠方法和建造器 API,簡化了 RetryPolicyBackOffPolicy 的創建 (從 Spring Retry 2.0.0 開始,對於某些策略)。 這使得配置更加清晰和流暢。

RetryPolicy 確定操作應何時重試SimpleRetryPolicy 用於重試固定次數。 嘗試次數包括初始嘗試。 對於 SimpleRetryPolicy,我們可以使用構造函數中的 maxAttempts 參數來設置最大嘗試次數。 這也包括配置零重試的功能 (new SimpleRetryPolicy(1)).

BackOffPolicy 用於控制重試嘗試之間的退避FixedBackOffPolicy 在繼續之前暫停固定時間。

5.3. 重試模板配置

讓我們使用新的 Builder API 在我們的 @Configuration 類中配置一個 RetryTemplate Bean:

@Configuration
public class AppConfig {

    private FixedBackOffPolicy fixedBackOffPolicy(long backOffPeriod) {
        FixedBackOffPolicy policy = new FixedBackOffPolicy();
        policy.setBackOffPeriod(backOffPeriod);
        return policy;
    }

    @Bean
    public RetryTemplate retryTemplate() {
        return RetryTemplate.builder()
          .maxAttempts(3)
          .customBackoff(fixedBackOffPolicy(2000L))
          .withListener(new DefaultListenerSupport())
          .build();
    }

    @Bean
    public RetryTemplate retryTemplateNoRetry() {
        return RetryTemplate.builder()
          .maxAttempts(1)
          .customBackoff(fixedBackOffPolicy(100L))
          .build();
    }
}

在示例中,我們使用了一個輔助方法來處理 FixedBackOffPolicy。 此外,我們使用 Builder API 的 RetryTemplate.builder(),它支持一個 maxAttempts() 方法,我們已將其配置為最多 3 次嘗試。 “maxAttempts” 包括初始嘗試和所有重試

此外,我們演示了新的 Builder API 接受 maxAttempts(1)RetryTemplateBuilder 構建器中。 maxAttempts()1 的值 意味着初始調用是唯一的嘗試,不會發生任何重試

5.4. 使用 RetryTemplate

要運行帶有重試處理的代碼,我們可以調用 retryTemplate.execute() 方法:

retryTemplate.execute(new RetryCallback<Void, RuntimeException>() {
    @Override
    public Void doWithRetry(RetryContext arg0) {
        myService.templateRetryService();
        ...
    }
});

與其使用匿名類,我們可以使用 lambda 表達式:

retryTemplate.execute(arg0 -> {
    myService.templateRetryService();
    return null;
});

6. 監聽器

監聽器提供在重試時產生的額外回調。我們還可以利用它們來處理不同重試嘗試中的各種跨關注點問題。

6.1. 添加回調

回調函數由 RetryListener 接口提供:

public class DefaultListenerSupport extends RetryListenerSupport {
    
    @Override
    public <T, E extends Throwable> void close(RetryContext context,
      RetryCallback<T, E> callback, Throwable throwable) {
        logger.info("onClose");
        //...
        super.close(context, callback, throwable);
    }

    @Override
    public <T, E extends Throwable> void onError(RetryContext context,
      RetryCallback<T, E> callback, Throwable throwable) {
        logger.info("onError"); 
        //...
        super.onError(context, callback, throwable);
    }

    @Override
    public <T, E extends Throwable> boolean open(RetryContext context,
      RetryCallback<T, E> callback) {
        logger.info("onOpen");
        //...
        return super.open(context, callback);
    }
}

openclose 回調在整個重試過程中分別在前後執行,而 onError 則應用於單個 RetryCallback 調用。

6.2. 註冊監聽器

我們使用 withListener(new DefaultListenerSupport()) 方法,將我們的監聽器 DefaultListenerSupport 註冊到 RetryTemplate Bean 上,如前所述。

7. 驗證結果

為了完成我們的示例,讓我們驗證結果:

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(
  classes = AppConfig.class,
  loader = AnnotationConfigContextLoader.class
)
public class SpringRetryIntegrationTest {

    @Autowired
    private MyService myService;
    @Autowired
    private RetryTemplate retryTemplate;

    @Test
    public void givenRetryService_whenCallWithException_thenRetry() {
        assertThrows(RuntimeException.class, () -> myService.retryService());
    }
}

正如從示例測試日誌中可以看出,我們已正確配置了 RetryTemplateRetryListener

2020-01-09 20:04:10 [main] INFO  o.b.s.DefaultListenerSupport - onOpen 
2020-01-09 20:04:10 [main] INFO  o.baeldung.springretry.MyServiceImpl
- throw RuntimeException in method templateRetryService() 
2020-01-09 20:04:10 [main] INFO  o.b.s.DefaultListenerSupport - onError 
2020-01-09 20:04:12 [main] INFO  o.baeldung.springretry.MyServiceImpl
- throw RuntimeException in method templateRetryService() 
2020-01-09 20:04:12 [main] INFO  o.b.s.DefaultListenerSupport - onError 
2020-01-09 20:04:12 [main] INFO  o.b.s.DefaultListenerSupport - onClose

8. 結論

在本文中,我們學習瞭如何使用 Spring Retry 通過註解、<em >RetryTemplate</em> 和回調監聽器進行配置。

user avatar
0 位用戶收藏了這個故事!
收藏

發佈 評論

Some HTML is okay.