在Spring Boot中輕松解決循環重試功能

2024年2月6日 22点热度 0人点赞

在遇到外部系統接口時可能遇到需要我們主動查詢外部系統的數據是否準備就緒的情況,然而外部系統可能在數據處理過程中非常耗時,不確定何時完成,外部系統又沒有完成通知的情況下就需要我們循環請求外部系統獲取結果。

舉例,通過簡單的HTTP請求查詢數據結果。

首先我們定義一個接口,該接口功能很簡單,根據嘗試的次數拋出異常或者返回ok。

@Slf4j
@RestController
@RequestMapping("/target")
public class TargetController {
 private static int NUMBER_OF_ATTEMPTS = 0;
 @GetMapping("/readiness")
 public String isDataReady() throws Exception {
  if (NUMBER_OF_ATTEMPTS < 5) {
   NUMBER_OF_ATTEMPTS  ;
   throw new Exception("data is not ready");
  }
  log.info("data is ready");
  NUMBER_OF_ATTEMPTS = 0;
  return "OK";
 }
}

下面創建一個ExternalService服務,該類中的checkWithDoWhile通過HTTP調用接口。

@Slf4j
@Service
public class ExternalService {
 static final String URL = "http://localhost:8080/target/readiness";
 @Autowired
 private RestTemplate restTemplate;
 public String checkWithDoWhile() {
  long timeToWait = 1000;
  int numRetry = 1;
  int maxAttempts = 10;
  boolean isDataReady = false;
  String readiness = null;
  do {
   log.info("tentative num: "   numRetry   " for getting the readiness of data from external service");
   try {
    HttpEntity<?> entity = new HttpEntity<Object>(null, null);
    ResponseEntity<String> response = restTemplate.exchange(URL, HttpMethod.GET, entity, String.class);
    readiness = response.getBody();
    isDataReady = true;
   } catch (Exception e) {
    try {
     Thread.sleep(timeToWait);
    } catch (InterruptedException exception) {
     log.error(e.getMessage());
    }
    numRetry  ;
   }
  } while (!isDataReady && numRetry <= maxAttempts);
  return readiness;
 }
}

現在我們把上面這段代碼換成Spring Retry試一下。

@Slf4j
@Service
public class ExternalService {
 static final String URL = "http://localhost:8080/target/readiness";
 @Autowired
 private RestTemplate restTemplate;
 @Retryable(retryFor = Exception.class, maxAttempts = 10, backoff = @Backoff(delay = 1000))
 public String checkWithRetry() {
  HttpEntity<?> entity = new HttpEntity<Object>(null, null);
  ResponseEntity<String> response = restTemplate.exchange(URL, HttpMethod.GET, entity, String.class);
  return response.getBody();
 }
}

@Retryable被註解的方法發生異常時會重試。它指定以下屬性:

  • retryFor = Exception.class:這意味著對於任何異常類型都應該重試該方法。
  • maxAttempts = 10:指定最多重試 10 次。
  • backoff = @Backoff(delay = 1000):它將重試之間的退避期設置為 1000 毫秒(1 秒)。

是不是代碼更加簡單幹凈。

使用Spring Retry需要添加以下依賴:

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

在配置類中,需要添加@EnableRetry註解,指定啟用Spring Retry

@EnableRetry
@Configuration
public class CommonConfig {
 @Bean
 public RestTemplate restTemplate() {
  return new RestTemplate();
 }
}