【SpringBoot框架篇】34.使用Spring Retry完成任务的重试

news2024/11/15 12:26:08

文章目录

  • 简要
  • 1.为什么需要重试?
  • 2.添加maven依赖
  • 3.使用@Retryable注解实现重试
  • 4.基于RetryTemplate模板实现重试

简要

Spring实现了一套重试机制,功能简单实用。Spring Retry是从Spring Batch独立出来的一个功能,已经广泛应用于Spring Batch,Spring Integration, Spring for Apache Hadoop等Spring项目。

本文将讲述如何使用Spring Retry及其实现原理。

1.为什么需要重试?

在调用一些第三方接口时候可能会因为网络或者服务方异常导致请求失败,这个时候可以通过重试解决这种问题。

以下面的简单的例子来了解 Retry的功能,下面有个doTask函数,执行该函数的时候如果出现异常则需要重试任务

  • 1.CountDownLatch 用于在主线程用于等待线程池中的任务完成
  • 2.AtomicInteger 类型用于计算重试次数
  • 3.ScheduledExecutorService用于定时执行需要重试的任务,如果没有异常则第一次执行完任务则会关闭线程池
  • 4.doTask函数中通过 1/0故意造成异常
    public static boolean doTask() {
        try {
            System.out.println(1/0);
            return true;
        } catch (Exception e) {
            return false;
        }
    }

    /**
     * 通过ScheduledExecutor定时器实现低配版本的重试机制
     * @param args
     * @throws Exception
     */
    public static void main(String[] args) throws Exception {
        CountDownLatch countDownLatch = new CountDownLatch(1);
        AtomicInteger count = new AtomicInteger(0);
        //设置重试的次数
        int retryNumber = 3;
        //创建单线程的定时任务处理器
        ScheduledExecutorService scheduledThreadPool = Executors.newSingleThreadScheduledExecutor();
        //(参数1=执行内容,参数2=初始延迟时间,参数3=任务间隔时间,参数4=时间单位)
        scheduledThreadPool.scheduleAtFixedRate(() -> {
            boolean flag = doTask();
            //业务是否处理成功,成功则关闭线程池
            if (flag || count.get() == retryNumber) {
                //执行成功或者已达到执行次数则关闭线程池
                scheduledThreadPool.shutdownNow();
                countDownLatch.countDown();;
            }else{
                log.info("第{}次重试任务", count.get()+1);
                count.getAndIncrement();
            }
        }, 0, 1, TimeUnit.SECONDS);
        //等待线程池中的任务完成
        countDownLatch.await();
    }

在这里插入图片描述
把doTask函数中的导致异常的代码注释再运行可以看到控制台没有打印重试的信息

从上面代码可以看出写一个任务重试的工具不难,感兴趣的同学可以通过AOP代理的方式自己实现基于注解的重试功能,Spring官方的Retry模块里有通过Aop加注解的方式实现重试功能,Aop玩腻了,我就不造轮子了。。

2.添加maven依赖

由于retry依赖中没有包含aspectj相关依赖,所以需要单独引用aspectj

    <!-- https://mvnrepository.com/artifact/org.springframework.retry/spring-retry -->
        <dependency>
            <groupId>org.springframework.retry</groupId>
            <artifactId>spring-retry</artifactId>
            <version>1.3.4</version>
        </dependency>

        <!--aop切面-->
        <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjweaver</artifactId>
            <version>1.9.4</version>
        </dependency>
        
      <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
        </dependency>

3.使用@Retryable注解实现重试

3.1.需要在springboot启动添加@EnableRetry注解开启对Retry的支持

@EnableRetry
@SpringBootApplication
public class RetryApplication {
}

3.2.定义需要重试任务的异常类型

public class CustomRecoveryException extends Exception{
    public CustomRecoveryException(String error){
        super(error);
    }
}

3.3.在需要任务重试的函数上面添加注解

  • value属性表示在哪些异常的情况下才会去重试,多个类型用,隔开。
  • maxAttempts属性设置执行次数,默认值为3则表示异常后只会重试两次
  • backoff属性设置下次重试的延迟时间,默认值为1000ms(1秒)。
@Slf4j
@Service
public class RetryServer  {
    @Retryable(value = {CustomRecoveryException.class, IOException.class}, maxAttempts = 3, backoff = @Backoff(delay = 1000))
    public void retryTest() throws CustomRecoveryException {
        log.info("retryTest,当前时间:{}",LocalDateTime.now());
        throw new CustomRecoveryException("test retry error");
    }

3.4.通过@Recover定义降级处理的函数
返回值需要和重试的任务一致,要不然会抛出异常。

    @Recover
    public void fallback(Throwable throwable) {
        // 降级处理逻辑
        log.error("fallback,Error msg:{}",throwable.getMessage());
        return "fallback";
    }
   } 

3.5.使用junit进行测试

@SpringBootTest
class RetryApplicationTests {

    @Autowired
    private RetryServer retryServer;

    @Test
    void contextLoads() throws CustomRecoveryException {
        retryServer.retryTest();
    }
}    

可以看到控制台只打印了三次日志,从这能确认任务共执行了三次,只重试了两次。
在这里插入图片描述

4.基于RetryTemplate模板实现重试

4.1.配置RetryTemplate重试的策略

@EnableRetry
@SpringBootApplication
public class RetryApplication {
    public static void main(String[] args) {
        SpringApplication.run(RetryApplication.class, args);
    }

    @Bean
    public RetryTemplate retryTemplate() {
        final SimpleRetryPolicy simpleRetryPolicy = new SimpleRetryPolicy();
        //设置最多执行次数, 包含第一次执行,下面配置成3,则第一次执行出现异常后最多会再重试2次
        simpleRetryPolicy.setMaxAttempts(3);
        final FixedBackOffPolicy fixedBackOffPolicy = new FixedBackOffPolicy();
        //设置重试间隔时间  单位 ms
        fixedBackOffPolicy.setBackOffPeriod(1000L);
        return RetryTemplate.builder()
                .customPolicy(simpleRetryPolicy)
                .customBackoff(fixedBackOffPolicy)
                .build();
    }
}

4.2.添加任务重试失败之后的降级处理回调函数

@Slf4j
@Component
public class CustomRecoveryCallback implements RecoveryCallback<String> {
    @Override
    public String recover(RetryContext retryContext) {
        log.error("fallback,retryCount:{},error msg:{}",retryContext.getRetryCount(),retryContext.getLastThrowable().getMessage());
        return "fallback";
    }
}

4.3.通过retryTemplate.execute执行需要重试的任务

@Slf4j
@Service
public class RetryServer  {

    @Autowired
    private RetryTemplate retryTemplate;

    @Autowired
    private CustomRecoveryCallback customRecoveryCallback;

    public void retryTemplateTest() {
        //第一个参数是只需要执行的方法,第二个参数是降级处理方法
        retryTemplate.execute(f->function(),customRecoveryCallback);
    }
    
  	/**
     * 具体的执行任务
     */
    public String function(){
        log.info("retryTemplateTest,当前时间:{}",LocalDateTime.now());
        throw new RuntimeException("test retry error");
    }

}

4.4.使用junit进行测试

@SpringBootTest
class RetryApplicationTests {

    @Autowired
    private RetryServer retryServer;

    @Test
    void contextLoads() {
        retryServer.retryTemplateTest();
    }
    
   }

可以看到测试得到的结果和注解的方式是一样的,都只执行了三次任务。
在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1353616.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Linux 进程和计划任务管理

一 内核功用&#xff1a;进程管理、内存管理、文件系统、网络功能、驱动程序、安全功能等 1 程序 是一组计算机能识别和执行的指令&#xff0c;运行于电子计算机上&#xff0c;满足人们某种需求的信息化工具 用于描述进程要完成的功能&#xff0c;是控制进程执行的指令集 2…

LeetCode 82:删除排序链表中的重复元素 II

一、题目描述 给定一个已排序的链表的头 head &#xff0c; 删除原始链表中所有重复数字的节点&#xff0c;只留下不同的数字 。返回 已排序的链表 。 示例 1&#xff1a; 输入&#xff1a;head [1,2,3,3,4,4,5] 输出&#xff1a;[1,2,5]示例 2&#xff1a; 输入&#xff1a…

B01、类加载子系统-02

JVM架构图-英文版 中文版见下图&#xff1a; 1、概述类的加载器及类加载过程 1.1、类加载子系统的作用 类加载器子系统负责从文件系统或者网络中加载Class文件,class文件在文件开头有特定的文件标识。ClassLoader只负责class文件的加载,至于它是否可以运行,则由Execution Engi…

炫酷按钮制作(HTML+CSS+Javascript)

实现效果&#xff1a; 当鼠标点击按钮时&#xff1a; 实现代码&#xff1a; <!DOCTYPE html> <html><head><meta charset"utf-8"><title></title><style>div{margin-top: 20px;margin-left: 20px;}.button{border: soli…

力扣热题100道-矩阵篇

矩阵 73.矩阵置零 给定一个 m x n 的矩阵&#xff0c;如果一个元素为 0 &#xff0c;则将其所在行和列的所有元素都设为 0 。请使用 原地 算法**。** 示例 1&#xff1a; 输入&#xff1a;matrix [[1,1,1],[1,0,1],[1,1,1]] 输出&#xff1a;[[1,0,1],[0,0,0],[1,0,1]]示例…

CSS 顶部位置翻转动画

<template><div class"container" mouseenter"startAnimation" mouseleave"stopAnimation"><!-- 旋方块 --><div class"box" :class"{ rotate-hor-top: isAnimating }"><!-- 元素内容 --><…

stable diffusion 基础教程-图生图

界面 图生图大概有以下几个功能: 图生图涂鸦绘制局部绘制局部绘制(涂鸦蒙版)其常用的也就上面四个,接下来逐步讲解。 以图反推提示词 图生图可以根据反推提示词来获取相应图片的提示词,目前3种主流方式,如下: CLIP反推提示词:推导出的文本倾向于自然语言的描述方式,…

Hive详解、配置、数据结构、Hive CLI

一、Hive 认识 1. Hive 应用 问题&#xff1a;公司的经营状况&#xff1f; 主题一&#xff1a;财务现金流指标1.1&#xff1a;净现金流入/流出量指标1.2&#xff1a;现金转换周期预算执行状况指标2.1&#xff1a;预算内成本控制指标2.2&#xff1a;预算与实际支出的差异 主题…

电路笔记 :自激振荡电路笔记 电弧打火机

三极管相关 三极管的形象描述 二极管 简单求解&#xff08;理想&#xff09; 优先导通&#xff08;理想&#xff09; 恒压降 稳压管&#xff08;二极管plus&#xff09; 基础工作模块 理想稳压管的工作特性 晶体管之三极管(“两个二极管的组合” ) 电弧打火机电路 1.闭合开…

docker 安装可视化工具 Portainer 以及 汉化

安装portainer是最新版本&#xff0c;汉化指定版本2.9.1 。如果要安装汉化版&#xff0c;可直接跳转步骤四 一、拉去镜像 安装网址&#xff1a;Install Portainer BE with Docker on Linux - Portainer Documentation docker pull portainer/portainer二、根据portainer镜像创建…

Centos7 安装zabbix6.0.25, agent2

Centos 7 版本&#xff08;不支持yum安装zabbix服务端&#xff0c;只能编译安装服务端&#xff09;zabbix 6.0 版本 版本6.0支持年限&#xff1a; Nginx 1.22.1 版本php 7.2.34 版本mariadb 10.5.19 版本 #关闭防火墙 [rootzabbix ~]# systemctl stop firewalld [rootzabbix…

STL-string

目录 &#x1f4a1;介绍 &#x1f4a1;string的基本操作 &#x1f4a1;string的构造函数 &#x1f4a1;string赋值操作 &#x1f4a1;string字符串拼接 &#x1f4a1;string的查找和替换 &#x1f4a1;string字符串比较 &#x1f4a1;string字符存取 &#x1f4a1;str…

Spark---RDD介绍

文章目录 1.Spark核心编程2.RDD介绍2.1.RDD基本原理2.2 RDD特点1.弹性2.分布式 &#xff1a;数据存储在大数据集群的不同节点上3.数据集 &#xff1a;RDD封装了计算逻辑&#xff0c;并不保存数据4.数据抽象 &#xff1a;RDD是一个抽象类&#xff0c;具体实现由子类来实现5. 不可…

C语言实例_math.h库函数功能及其用法详解

一、前言 数学在计算机编程中扮演着至关重要的角色&#xff0c;C语言的math.h头文件提供了一系列的函数和工具&#xff0c;用于数学计算和常用数学函数的实现。这些函数包括数值运算、三角函数、指数对数函数等&#xff0c;为开发人员提供了强大的数学处理能力。本文将对math.…

字符编码转换

宽窄字符和字符编码的关系 多字节(窄)字符&#xff1a;在C/C中&#xff0c;char是一种数据类型&#xff0c;规定sizeof(char)1&#xff0c;即一个char占用一个字节&#xff0c;1Byte8bit。并没有规定一个char就要与ASCII对应&#xff0c;不过&#xff0c;通常情况下char值与AS…

LeetCode 每日一题 Day 32 ||递归单调栈

2487. 从链表中移除节点 给你一个链表的头节点 head 。 移除每个右侧有一个更大数值的节点。 返回修改后链表的头节点 head 。 示例 1&#xff1a; 输入&#xff1a;head [5,2,13,3,8] 输出&#xff1a;[13,8] 解释&#xff1a;需要移除的节点是 5 &#xff0c;2 和 3 。…

【RocketMQ每日一问】RocketMQ SQL92过滤用法以及原理?

1.生产端 public class SQLProducer {public static int count 10;public static String topic "xiao-zou-topic";public static void main(String[] args) {DefaultMQProducer producer MQUtils.createLocalProducer();IntStream.range(0, count).forEach(i -&g…

【算法】使用位运算解算法题(C++)

文章目录 0. 位运算 基本介绍1. 位运算基本使用 连带题目191.位1的个数338.比特位计数461.汉明距离136.只出现一次的数字260.只出现一次的数字III 2. 使用位运算解决算法题面试题01.01.判定字符是否唯一371.两整数之和137.只出现一次的数字II面试题17.04.消失的数字面试题17.1…

2023年12月GESP C++七级编程题转Python真题解析

七、2023年12月GESP C(Python)七级编程题 2023年12月GESP Python最高六级&#xff0c;但C与Python同级编程题相同。本篇引用2023年12月GESPC七级编程题&#xff0c;用Python实现。 【七级编程题1】 【试题名称】&#xff1a;商品交易 时间限制&#xff1a;1.0 s 内存限制&…

上海AI lab大模型微调

教程链接&#xff1a;InternLM学习教程链接 命令行演示结果&#xff1a; web演示结果