Java Spring Boot – @Retryable 重試機制

文章最後更新於 2023 年 3 月 28 日

前言

Java Spring Boot是一個非常流行的開源框架,它提供了很多便捷的功能來簡化開發過程。其中一個非常有用的功能就是@Retryable,它可以讓開發人員輕鬆地實現方法的重試機制。本文將介紹@Retryable的優缺點、使用方法、注意事項以及範例。

@Retryable是Spring Retry模塊提供的注解之一,它可以在方法執行時發生錯誤時自動進行重試,直到達到最大重試次數或執行成功為止。@Retryable的優點是它可以大大提高方法的可靠性,因為即使發生了錯誤,它也會自動進行重試,直到方法執行成功。然而,@Retryable的缺點是它可能會導致無限循環,因此開發人員需要根據實際情況設置最大重試次數和重試間隔時間。

@Retryable的參數介紹

  1. value:指定需要重試的異常類型。如果沒有指定,則默認重試所有異常。可以指定多個異常類型,以陣列的形式提供。
  2. maxAttempts:指定最多重試次數。默認值為3。
  3. backoff:指定重試間隔時間。可以指定一個@Backoff註釋對象,這個對象有兩個屬性:
    • delay:指定重試間隔時間,默認為0毫秒。
    • multiplier:指定每次重試的間隔時間增加倍數,默認為1。
  4. include:指定需要重試的異常類型。與value類似,但是是針對於某些異常類型進行重試。
  5. exclude:指定不需要重試的異常類型。與value類似,但是是針對於某些異常類型不進行重試。

使用方法與範例

使用@Retryable非常簡單,只需要在要進行重試的方法上添加@Retryable注解,然後設置相關的重試參數即可。但記得要加上@EnableRetry例如:

import org.springframework.retry.annotation.EnableRetry;
import org.springframework.retry.annotation.Retryable;

@EnableRetry
public class MyService {
    
    @Retryable(maxAttempts = 3)
    public void doSomething() {
        // your code here
    }
}

在這個例子中,如果 doSomething() 方法在第一次執行時失敗了,Spring Retry 將再次調用該方法,最多嘗試 3 次。

再一個例子:

@Retryable(value = {SQLException.class}, maxAttempts = 3, backoff = @Backoff(delay = 1000))
public void retryMethod() throws SQLException {
    // method implementation
}

上面的範例中,當方法執行遇到SQLException時,@Retryable會自動進行最多3次的重試,每次重試間隔1秒。

以下是幾個使用@Retryable的範例:

  1. 重試讀取文件的方法:
@Retryable(value = {IOException.class}, maxAttempts = 3, backoff = @Backoff(delay = 1000))
public String readFile(String fileName) throws IOException {
    BufferedReader br = new BufferedReader(new FileReader(fileName));
    StringBuilder sb = new StringBuilder();
    String line = br.readLine();

    while (line != null) {
        sb.append(line);
        sb.append(System.lineSeparator());
        line = br.readLine();
    }
    return sb.toString();
}

  1. 重試向API發送請求的方法:
@Retryable(value = {RestClientException.class}, maxAttempts = 3, backoff = @Backoff(delay = 1000))
public ResponseEntity<String> sendRequest(String url, HttpMethod method, HttpEntity<?> requestEntity, Class<String> responseType) throws RestClientException {
    return restTemplate.exchange(url, method, requestEntity, responseType);
}

  1. 重試向資料庫寫入數據的方法:
@Retryable(value = {SQLException.class}, maxAttempts = 3, backoff = @Backoff(delay = 1000))
public void writeDataToDatabase(Data data) throws SQLException {
    // 向資料庫寫入數據的邏輯
}

這個方法會在寫入失敗時進行最多三次重試,重試間隔為1秒。

使用@Retryable的注意事項

需要注意的是,如果使用@Retryable注解,方法返回值的類型必須為void或與原方法返回值類型相同。如果要進行一些自定義邏輯,例如日誌輸出、記錄失敗次數等,可以使用Spring提供的RetryCallback和RecoveryCallback接口進行實現。

在使用@Retryable注解時,需要確保使用的異常類型是可重試的,例如由於網路問題、資料庫連接池等原因引起的異常。對於無法重試的異常類型,應該避免使用@Retryable注解。

此外@Retryable的使用注意事項還包括:

  1. 要確保@Retryable注解的方法為無狀態方法,不應該包含對象狀態。如果方法需要狀態,建議使用Spring Retry提供的StatefulRetryOperationsInterceptor。
  2. 設置最大重試次數和重試間隔時間時,需要根據實際情況進行設置。過多的重試次數和短的重試間隔時間可能會導致無限循環。
  3. 在使用@Retryable時,建議同時設置@Recover注解,用於定義當所有重試都失敗後的後備方案。例如:
@Retryable(value = {SQLException.class}, maxAttempts = 3, backoff = @Backoff(delay = 1000))
public void retryMethod() throws SQLException {
    // method implementation
}

@Recover
public void recover(SQLException e) {
    // fallback implementation
}

當所有重試都失敗後,將會調用recover方法。

@Retryable 失效的可能原因

如果發現 @Retryable 不生效,可能有以下一些原因:

  1. 忘記加上 @EnableRetry 注解:在 Spring Boot 中,如果想要使用 @Retryable 注解,必須在 Spring 配置類上加上 @EnableRetry 注解,啟用 Spring Retry 的支持。
  2. 异常类型不匹配:如果在 @Retryable 注解中指定的异常类型与方法中抛出的异常类型不匹配,那麼重試操作就不會發生,可以嘗試在 @Retryable 注解中添加需要重試的异常類型。
  3. 重試策略配置錯誤:Spring Retry 預設的重試策略是重試 3 次,每次重試之間等待 1 秒。如果需要自定義重試策略,可以實現 RetryPolicy 接口,並在 @Retryable 注解中指定重試策略。
  4. 事務配置錯誤:如果被 @Retryable 注解標註的方法中包含事務操作,那麼可能需要進一步配置事務管理器以支持重試操作。
  5. 不在 Spring 管理的對象上使用:@Retryable 注解只能用於 Spring 管理的對象上,如果在非 Spring 管理的對象上使用,則重試操作不會生效。

如果以上方法都沒有解決問題,可以考慮打開 Spring Retry 的日誌調試模式,查看詳細的日誌信息,以便進一步排查問題。

總結一下,@Retryable是一個非常實用的功能,它可以提高方法的可靠性,減少錯誤率。開發人員可以根據實際情況使用它,設置合適的重試次數和重試間隔時間,同時建議設置@Recover注解,用於定義後備方案。

關於作者

卡哥
卡哥
我是Oscar (卡哥),前Yahoo Lead Engineer、高智商同好組織Mensa會員,超過十年的工作經驗,服務過Yahoo關鍵字廣告業務部門、電子商務及搜尋部門,喜歡彈吉他玩音樂,也喜歡投資美股、虛擬貨幣,樂於與人分享交流!

如果對文章內容有任何問題,歡迎在底下留言讓我知道。
如果你喜歡我的文章,可以按分享按鈕,讓更多的人看見我的文章。

網友留言