开发者

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

开发者 https://www.devze.com 2022-12-17 10:24 出处:网络 作者: JAVA旭阳
目录前言Spring-Retry介绍使用安装依赖启用重试功能应用验证总结前言 作为后端程序员,我们的日常工作就是调用一些第三方服务,将数据存入数据库,返回信息给前端。但你不能保证所有的事情一直都很顺利。像有些第三方
目录
  • 前言
  • Spring-Retry介绍使用
    • 安装依赖
    • 启用重试功能
    • 应用
    • 验证
  • 总结

    前言

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

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

    public interface OutSource {
        List<Integer> getResult() throws TimeOutException;
    }
    
    @Service
    public class OutSourceImpl implements OutSource {
    
        static Random random = new Random();
        @O编程verride
        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
      http://www.devze.com  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();
        javascript@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;
    
        // 看这里
        @Randroidetryable(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.RetryableSjavascriptervice  : 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 探索。

    到此这篇关于详解如何在SpringBoot中优雅地重试调用第三方API的文章就介绍到这了,更多相关SpringBoot重试调用第三方API内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

    0

    精彩评论

    暂无评论...
    验证码 换一张
    取 消

    关注公众号