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

简介: 如何在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更多的使用建议可以自己去官网github.com/spring-proj… 探索。

本文讲解了JDK中提供的两种生成随机数的方式,一个是JDK 1.0引入的Random类,另外一个是JDK1.7引入的ThreadLocalRandom类,由于底层的实现机制不同,ThreadLocalRandom的性能是远高于Random,建议后面大家在技术选型的时候优先使用ThreadLocalRandom

目录
相关文章
|
18天前
|
监控 容灾 测试技术
第三方API的稳定性如何保障?
第三方API的稳定性对电商至关重要,直接影响业务连续性和用户体验。为确保稳定,可采取以下措施:选择可靠提供商、评估技术架构高可用性、实现限流重试机制、实时监控预警、充分测试、优化网络连接、使用API网关、保持与提供商沟通、制定应急预案及定期评估改进。这些方法能有效提升API稳定性,保障业务高效运行和用户满意度。
35 0
|
18天前
|
运维 数据挖掘 API
官方api和第三方api有什么区别
官方API与第三方API是电商商家常用的两种接口类型。官方API由电商平台提供,功能全面、集成度高、安全性强且稳定性好,适合处理核心业务;而第三方API由外部服务商提供,专注特定功能如支付、物流等,灵活性和扩展性更强,但安全性和稳定性参差不齐。商家应根据自身需求、预算及技术能力选择合适的API,确保数据安全与合规性,同时优化运营流程、提升用户体验并拓展业务功能,在竞争中占据优势。
33 0
|
3月前
|
前端开发 Cloud Native Java
Java||Springboot读取本地目录的文件和文件结构,读取服务器文档目录数据供前端渲染的API实现
博客不应该只有代码和解决方案,重点应该在于给出解决方案的同时分享思维模式,只有思维才能可持续地解决问题,只有思维才是真正值得学习和分享的核心要素。如果这篇博客能给您带来一点帮助,麻烦您点个赞支持一下,还可以收藏起来以备不时之需,有疑问和错误欢迎在评论区指出~
Java||Springboot读取本地目录的文件和文件结构,读取服务器文档目录数据供前端渲染的API实现
|
8月前
|
Java API 数据库
构建RESTful API已经成为现代Web开发的标准做法之一。Spring Boot框架因其简洁的配置、快速的启动特性及丰富的功能集而备受开发者青睐。
【10月更文挑战第11天】本文介绍如何使用Spring Boot构建在线图书管理系统的RESTful API。通过创建Spring Boot项目,定义`Book`实体类、`BookRepository`接口和`BookService`服务类,最后实现`BookController`控制器来处理HTTP请求,展示了从基础环境搭建到API测试的完整过程。
129 4
|
4月前
|
机器学习/深度学习 人工智能 Serverless
👉「免费满血DeepSeek实战-联网搜索×Prompt秘籍|暨6平台横评」
满血 DeepSeek 免费用!支持联网搜索!创作声明:真人攥写-非AI生成,Written-By-Human-Not-By-AI
3454 10
👉「免费满血DeepSeek实战-联网搜索×Prompt秘籍|暨6平台横评」
|
8月前
|
IDE Java API
基于Spring Boot REST API设计指南
【10月更文挑战第4天】 在现代的软件开发中,RESTful API已经成为了构建网络应用的标准之一。它通过HTTP协议提供了与资源交互的方式,使得不同的应用程序能够进行数据交互。Spring Boot作为一个功能强大的框架,它简化了配置和开发流程,成为了构建RESTful API的理想选择。本文将详细介绍如何在Spring Boot中设计和实现高质量的RESTful API,并提供一些最佳实践。
153 1
|
8月前
|
缓存 Java API
基于Spring Boot REST API设计指南
【10月更文挑战第11天】 在构建现代Web应用程序时,RESTful API已成为一种标准,使得不同的应用程序能够通过HTTP协议进行通信,实现资源的创建、读取、更新和删除等操作。Spring Boot作为一个功能强大的框架,能够轻松创建RESTful API。本文将详细介绍如何在Spring Boot中设计和实现高质量的RESTful API。
308 61
|
6月前
|
存储 安全 Java
Spring Boot 编写 API 的 10条最佳实践
本文总结了 10 个编写 Spring Boot API 的最佳实践,包括 RESTful API 设计原则、注解使用、依赖注入、异常处理、数据传输对象(DTO)建模、安全措施、版本控制、文档生成、测试策略以及监控和日志记录。每个实践都配有详细的编码示例和解释,帮助开发者像专业人士一样构建高质量的 API。
205 9
|
7月前
|
Java 测试技术 API
详解Swagger:Spring Boot中的API文档生成与测试工具
详解Swagger:Spring Boot中的API文档生成与测试工具
422 4
|
2月前
|
JSON 数据挖掘 API
1688API最新指南:商品详情接口接入与应用
本指南介绍1688商品详情接口的接入与应用,该接口可获取商品标题、价格、规格、库存等详细信息,适用于电商平台开发、数据分析等场景。接口通过商品唯一标识查询,支持HTTP GET/POST请求,返回JSON格式数据,助力开发者高效利用1688海量商品资源。