如何在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项目使用方式 ...
随机推荐
- pgsql 的问题
pgsql 怎么插入inet类型的数据?insert into table (remote_addr) values ( ?::INET); pgsql如何截取时间的精度 select create ...
- Atcoder CODE FESTIVAL 2016 Grand Final E - Water Distribution
Atcoder CODE FESTIVAL 2016 Grand Final E - Water Distribution 题目链接:https://atcoder.jp/contests/cf16- ...
- Xray
Xray基础操作 代理设置 运行xray.exe xray.exe genca 运行后会生成ca.crt和cr.key 浏览器导入证书 设置代理7777端口 第一次启动 xray 之后,当前目录会生成 ...
- 前端开发日常——CSS动画无限轮播
近来没有什么值得写的东西,空闲的时候帮前端的同学做了些大屏上的展示模块,就放在这里写写吧,手把手"需求->设计-> 实现",受众偏新手向. 为了直观便于理解, 直接把结 ...
- Linux基础_6_文本编辑
vi i #编辑 ESC+:wq #保存退出 ESC+ZZ #保存退出 ESC+:q! #不保存退出 shift+z+q #不保存退出 dd #删除所在行 ESC+u #撤销dd误操作 :/字符串 # ...
- Vue中组件化编码使用(实战练习一)
Vue中组件化编码的大致流程(初接触).组件之间的参数传递(最基础的形式).组件之间的配合完成一个需求 1.在Vue中进行组件化编码 1.1.组件化编码流程: (1).拆分静态组件:组件要按照功能点拆 ...
- SpringBoot→Maven项目快速搭建
使用软件 :SpringToolSuite4 打开软件后在Package Explorer 栏中点击右键 Spring starter project 等待反应 填写完毕之后点击next fi ...
- Linux的挖矿木马病毒清除(kswapd0进程)
1.top查看资源使用情况 看到这些进程一直在变化,但是,主要是由于kswapd0进程在作怪,占据了99%以上的CUP,查找资料后,发现它就是挖矿进程 2.排查kswapd0进程 执行命令netsta ...
- Landau-Vishkin
基础算法 假设我们有两个字符串:,每个字符串由A C G T四个字母组成. 在两个字符串上,都有三种可能的编辑操作(突变): 删除某个字符 在某个位置插入字符 改变某个字符 每一个编辑操作都有惩罚值. ...
- CSS line-break属性与中文标点换行
关于标点符号把文字带着换行的问题解决方案 出现的问题 最近在弄一个介绍页面的时候遇到一个很巧的问题,在文本换行的时候刚好能够放下文字,但是标点符号把这个文字带着换行了 如下图所示: 如果的在上一行显示 ...