如何在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
异常会进行重试,最大重试maxAttempts
3次。
验证
这一次,当我们访问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旭阳
更多学习资料请移步:程序员成神之路
如何在SpringBoot中优雅地重试调用第三方API?的更多相关文章
- 如何在php中优雅的地调用python程序
1.准备工作 安装有python和php环境的电脑一台. 2.书写程序. php程序如下 我们也可以将exec('python test.py') 换成 system('python test.p ...
- 【Chrome】如何在C++中增加给JavaScript调用的API
本文示例说明了如何在Chrome浏览器中增加JavaScript API.为了简化,先假设是在已有的namespace中增加一个新的API,文章的最后将指出如果增加一下全新的namespace所需注意 ...
- 如何在MyBatis中优雅的使用枚举
问题 在编码过程中,经常会遇到用某个数值来表示某种状态.类型或者阶段的情况,比如有这样一个枚举: public enum ComputerState { OPEN(10), //开启 CLOSE( ...
- 如何在SpringBoot中使用JSP ?但强烈不推荐,果断改Themeleaf吧
做WEB项目,一定都用过JSP这个大牌.Spring MVC里面也可以很方便的将JSP与一个View关联起来,使用还是非常方便的.当你从一个传统的Spring MVC项目转入一个Spring Boot ...
- 如何在 Swoole 中优雅的实现 MySQL 连接池
如何在 Swoole 中优雅的实现 MySQL 连接池 一.为什么需要连接池 ? 数据库连接池指的是程序和数据库之间保持一定数量的连接不断开, 并且各个请求的连接可以相互复用, 减少重复连接数据库带来 ...
- 在Angular.js中的H5页面调用Web api时跨域问题处理
/// <summary> /// 被请求时 /// 在Angular.js中的H5页面调用Web api时跨域问题处理 /// </summary> /// <para ...
- Java 代码中如何调用 第三方Api
在代码中调用第三方API 获取数据 package com.example.demo.utils; import com.alibaba.fastjson.JSONObject; import lom ...
- vue 服务代理 调用第三方api
项目中前期需要调用第三方API来获取汇率.因为直接调用会有跨域的问题,所以使用来服务代理. 在config配置代理可以这样写: 而调用接口就可以这样写: 坑:配置完成后一直报500,开始怀疑人生.最后 ...
- 你知道如何在springboot中使用redis吗
特别说明:本文针对的是新版 spring boot 2.1.3,其 spring data 依赖为 spring-boot-starter-data-redis,且其默认连接池为 lettuce ...
- spring-boot+mybatis开发实战:如何在spring-boot中使用myabtis持久层框架
前言: 本项目基于maven构建,使用mybatis-spring-boot作为spring-boot项目的持久层框架 spring-boot中使用mybatis持久层框架与原spring项目使用方式 ...
随机推荐
- Handler机制与生产者消费者模式
本文梳理了 Handler 的源码,并详细阐述了 Handler 与生产者消费者模式的关系,最后给出了多版自定义 Handler 实现.本文首发于简书,重新整理发布. 一.Handler Handle ...
- Java对象或String转JSON对象
Java String转JSON对象 用阿里的fastjson里的一个方法,导入fastjson包JSONObject jsonObject1 =JSONObject.parseObject(Stri ...
- Springboot 之 Filter 实现超大响应 JSON 数据压缩
简介 项目中,请求时发送超大 json 数据外:响应时也有可能返回超大 json数据.上一篇实现了请求数据的 gzip 压缩.本篇通过 filter 实现对响应 json 数据的压缩. 先了解一下以下 ...
- Kafka之安装
Kafka之安装 一.下载kafka 此博客只讲述kafka0.8和kafka1.0两个版本 更改kafka下的/home/bigdata/kafka/config/server.properties ...
- python-D3-语法入门1
Python语法注释 什么是注释 注释其实就是对一段代码的解释说明(注释是代码之母) 如何编写注释 方式1:解释说明文字前加警号 (pycharm中有快捷键ctrl+?) # 注释(单行注释) 方式2 ...
- 2.ElasticSearch系列之集群权限认证
1. 在master节点上创建秘钥库 export ES_PATH_CONF="/home/elasticsearch/config" && /usr/local/ ...
- Autobus 方法记录
原题链接 [COCI2021-2022#4] Autobus 题目描述 在一个国家里有 \(n\) 座城市.这些城市由 \(m\) 条公交线路连接,其中第 \(i\) 条线路从城市 \(a_i\) 出 ...
- HTML元素大全(1)
01.基础元素 <h1/2/3/4/5/6>标题 从大h1到小h6,块元素,有6级标题.是一种标题类语义标签,内置了字体.边距样式. 合理使用h标签,主要用于标题,不要为了加粗效果而随意使 ...
- 第二阶段:高级核心基础知识·第4章shell特性·2
1.统计日志,日志内容 39.96.187.239 - - [11/Nov/2019:10:08:01 +0800] "GET / HTTP/1.1" 302 0 "-& ...
- golang中的锁竞争问题
索引:https://www.waterflow.link/articles/1666884810643 当我们打印错误的时候使用锁可能会带来意想不到的结果. 我们看下面的例子: package ma ...