
在遇到外部系统接口时可能遇到需要我们主动查询外部系统的数据是否准备就绪的情况,然而外部系统可能在数据处理过程中非常耗时,不确定何时完成,外部系统又没有完成通知的情况下就需要我们循环请求外部系统获取结果。
举例,通过简单的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();
}
}