前言

作为后端程序员,我们的日常工作就是调用一些第三方服务,将数据存入数据库,返回信息给前端。但你不能保证所有的事情一直都很顺利。像有些第三方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旭阳

更多学习资料请移步:程序员成神之路

如何在SpringBoot中优雅地重试调用第三方API?的更多相关文章

  1. 如何在php中优雅的地调用python程序

    1.准备工作   安装有python和php环境的电脑一台. 2.书写程序. php程序如下 我们也可以将exec('python test.py') 换成 system('python test.p ...

  2. 【Chrome】如何在C++中增加给JavaScript调用的API

    本文示例说明了如何在Chrome浏览器中增加JavaScript API.为了简化,先假设是在已有的namespace中增加一个新的API,文章的最后将指出如果增加一下全新的namespace所需注意 ...

  3. 如何在MyBatis中优雅的使用枚举

    问题 在编码过程中,经常会遇到用某个数值来表示某种状态.类型或者阶段的情况,比如有这样一个枚举:   public enum ComputerState { OPEN(10), //开启 CLOSE( ...

  4. 如何在SpringBoot中使用JSP ?但强烈不推荐,果断改Themeleaf吧

    做WEB项目,一定都用过JSP这个大牌.Spring MVC里面也可以很方便的将JSP与一个View关联起来,使用还是非常方便的.当你从一个传统的Spring MVC项目转入一个Spring Boot ...

  5. 如何在 Swoole 中优雅的实现 MySQL 连接池

    如何在 Swoole 中优雅的实现 MySQL 连接池 一.为什么需要连接池 ? 数据库连接池指的是程序和数据库之间保持一定数量的连接不断开, 并且各个请求的连接可以相互复用, 减少重复连接数据库带来 ...

  6. 在Angular.js中的H5页面调用Web api时跨域问题处理

    /// <summary> /// 被请求时 /// 在Angular.js中的H5页面调用Web api时跨域问题处理 /// </summary> /// <para ...

  7. Java 代码中如何调用 第三方Api

    在代码中调用第三方API 获取数据 package com.example.demo.utils; import com.alibaba.fastjson.JSONObject; import lom ...

  8. vue 服务代理 调用第三方api

    项目中前期需要调用第三方API来获取汇率.因为直接调用会有跨域的问题,所以使用来服务代理. 在config配置代理可以这样写: 而调用接口就可以这样写: 坑:配置完成后一直报500,开始怀疑人生.最后 ...

  9. 你知道如何在springboot中使用redis吗

    特别说明:本文针对的是新版 spring boot 2.1.3,其 spring data 依赖为 spring-boot-starter-data-redis,且其默认连接池为 lettuce ​  ...

  10. spring-boot+mybatis开发实战:如何在spring-boot中使用myabtis持久层框架

    前言: 本项目基于maven构建,使用mybatis-spring-boot作为spring-boot项目的持久层框架 spring-boot中使用mybatis持久层框架与原spring项目使用方式 ...

随机推荐

  1. Handler机制与生产者消费者模式

    本文梳理了 Handler 的源码,并详细阐述了 Handler 与生产者消费者模式的关系,最后给出了多版自定义 Handler 实现.本文首发于简书,重新整理发布. 一.Handler Handle ...

  2. Java对象或String转JSON对象

    Java String转JSON对象 用阿里的fastjson里的一个方法,导入fastjson包JSONObject jsonObject1 =JSONObject.parseObject(Stri ...

  3. Springboot 之 Filter 实现超大响应 JSON 数据压缩

    简介 项目中,请求时发送超大 json 数据外:响应时也有可能返回超大 json数据.上一篇实现了请求数据的 gzip 压缩.本篇通过 filter 实现对响应 json 数据的压缩. 先了解一下以下 ...

  4. Kafka之安装

    Kafka之安装 一.下载kafka 此博客只讲述kafka0.8和kafka1.0两个版本 更改kafka下的/home/bigdata/kafka/config/server.properties ...

  5. python-D3-语法入门1

    Python语法注释 什么是注释 注释其实就是对一段代码的解释说明(注释是代码之母) 如何编写注释 方式1:解释说明文字前加警号 (pycharm中有快捷键ctrl+?) # 注释(单行注释) 方式2 ...

  6. 2.ElasticSearch系列之集群权限认证

    1. 在master节点上创建秘钥库 export ES_PATH_CONF="/home/elasticsearch/config" && /usr/local/ ...

  7. Autobus 方法记录

    原题链接 [COCI2021-2022#4] Autobus 题目描述 在一个国家里有 \(n\) 座城市.这些城市由 \(m\) 条公交线路连接,其中第 \(i\) 条线路从城市 \(a_i\) 出 ...

  8. HTML元素大全(1)

    01.基础元素 <h1/2/3/4/5/6>标题 从大h1到小h6,块元素,有6级标题.是一种标题类语义标签,内置了字体.边距样式. 合理使用h标签,主要用于标题,不要为了加粗效果而随意使 ...

  9. 第二阶段:高级核心基础知识·第4章shell特性·2

    1.统计日志,日志内容 39.96.187.239 - - [11/Nov/2019:10:08:01 +0800] "GET / HTTP/1.1" 302 0 "-& ...

  10. golang中的锁竞争问题

    索引:https://www.waterflow.link/articles/1666884810643 当我们打印错误的时候使用锁可能会带来意想不到的结果. 我们看下面的例子: package ma ...