如何在SpringBoot中优雅地重试调用第三方API?

前言

作为后端程序员,我们的日常工作就是调用一些第三方服务,将数据存入数据库,返回信息给前端。但你不能保证所有的事情一直都很顺利。像有些第三方API,偶尔会出现超时。此时,我们要重试几次,这取决于你的重试策略。

下面举一个我在日常开发中多次看到的例子:

public interface OutSource {     List<Integer> getResult() throws TimeOutException; }  @Service public class OutSourceImpl implements OutSource {      static Random random = new Random();     @Override     public List<Integer> getResult() {         //mock failure         if (random.nextInt(2) == 1)             throw new TimeOutException();         return List.of(1, 2, 3);     } }   @Slf4j @Service public class ManuallyRetryService {      @Autowired     private OutSource outSource;      public List<Integer> getOutSourceResult(String data, int retryTimes) {         log.info("trigger time:{}", retryTimes);          if (retryTimes > 3) {             return List.of();         }          try {             List<Integer> lst = outSource.getResult();             if (!CollectionUtils.isEmpty(lst)) {                 return lst;             }              log.error("getOutSourceResult error, data:{}", data);         } catch (TimeOutException e) {             log.error("getOutSourceResult timeout", e);         }         // 递归调用         return getOutSourceResult(data, retryTimes + 1);     }  }  @Slf4j @RestController public class RetryTestController {      @Autowired     private ManuallyRetryService manuallyRetryService;          @GetMapping("manually")     public String manuallyRetry() {         List<Integer> result = manuallyRetryService.getOutSourceResult("haha", 0);         if (!CollectionUtils.isEmpty(result)) {             return "ok";         }         return "fail";     } } 

看看上面这段代码,我认为它可以正常工作,当retryTimes达到4时,无论如何我们都会得到最终结果。但是你觉得写的好吗?优雅吗?下面我来介绍Spring中的一个组件:spring-retry,我们不妨来试一试。

Spring-Retry介绍使用

spring-retry是Spring中的提供的一个重试框架,提供了注解的方式,在不入侵原有业务逻辑代码的方式下,优雅的实现重处理功能。

安装依赖

  • 如果你的是gradle应用,引入下面的依赖
implementation 'org.springframework.boot:spring-boot-starter-aop''org.springframework.boot:spring-boot-starter-aop' implementation 'org.springframework.retry:spring-retry' 
  • 如果你的项目使用的是maven项目,引入下面的依赖
<dependency>     <groupId>org.springframework.retry</groupId>     <artifactId>spring-retry</artifactId> </dependency>  <dependency>     <groupId>org.springframework.boot</groupId>     <artifactId>spring-boot-starter-aop</artifactId> </dependency> 

启用重试功能

添加@EnableRetry注解在入口的类上从而启用功能。

@SpringBootApplication //看过来 @EnableRetry public class TestSpringApplication {      public static void main(String[] args) {         SpringApplication.run(TestSpringApplication.class, args);     }  } 

应用

我们以前面的为例,看看怎么使用,如下面的代码:

public interface OutSource {     List<Integer> getResult() throws TimeOutException; }  @Service public class OutSourceImpl implements OutSource {      static Random random = new Random();     @Override     public List<Integer> getResult() {         //mock failure will throw an exception every time         throw new TimeOutException();     } }  @Slf4j @Service public class RetryableService {      @Autowired     private OutSource outSource;      // 看这里     @Retryable(value = {TimeOutException.class}, maxAttempts = 3)     public List<Integer> getOutSourceResult(String data) {         log.info("trigger timestamp:{}", System.currentTimeMillis() / 1000);          List<Integer> lst = outSource.getResult();         if (!CollectionUtils.isEmpty(lst)) {             return lst;         }         log.error("getOutSourceResult error, data:{}", data);          return null;     }  }   @Slf4j @RestController public class RetryTestController {      @Autowired     private RetryableService retryableService;      @GetMapping("retryable")     public String manuallyRetry2() {         try {             List<Integer> result = retryableService.getOutSourceResult("aaaa");             if (!CollectionUtils.isEmpty(result)) {                 return "ok";             }         } catch (Exception e) {             log.error("retryable final exception", e);         }         return "fail";     }  } 
  • 关键在于Service层中的实现类中添加了 @Retryable注解,实现了重试, 指定value是TimeOutException异常会进行重试,最大重试maxAttempts3次。

验证

这一次,当我们访问http://localhost:8080/retryable时,我们将看到浏览器上的结果失败。然后在你的终端上看到:

INFO 66776 --- [nio-9997-exec-1] c.m.testspring.service.RetryableService  : trigger timestamp:1668236840  INFO 66776 --- [nio-9997-exec-1] c.m.testspring.service.RetryableService  : trigger timestamp:1668236841  INFO 66776 --- [nio-9997-exec-1] c.m.testspring.service.RetryableService  : trigger timestamp:1668236842 ERROR 66776 --- [nio-9997-exec-1] c.m.t.controller.RetryTestController     : retryable final exception 

总结

本文分享了spring-retry重试框架最基础的使用,可以无侵入业务代码进行重试。关于spring-retry更多的使用建议可以自己去官网https://github.com/spring-projects/spring-retry 探索。

如果本文对你有帮助的话,请留下一个赞吧
欢迎关注个人公众号——JAVA旭阳
更多学习资料请移步:程序员成神之路

发表评论

相关文章