01-Spring实现重试和降级机制

news2024/11/16 16:49:09

主要用于在模块调用中,出现失败、异常情况下,仍需要进行重复调用。并且在最终调用失败时,可以采用降级措施,返回一般结果。

1、重试机制

我们采用spring 提供的retry 插件,其原理采用aop机制,所以需要额外引入starter-aop包

1、依赖引入

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-aop</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.retry</groupId>
    <artifactId>spring-retry</artifactId>
</dependency>

2、在主启动类或者需要重试的方法所在的类上添加注解@EnableRetry

3、在需要重试的方法上增加注解 @Retryable,表示该方法需要重试。可以定义在接口的抽象方法上,也可以定义在实际的具体方法上。

public interface RetryService {
    /**
     * 指定异常CustomRetryException重试,重试最大次数为4(默认是3),重试补偿机制间隔200毫秒
     * 还可以配置exclude,指定异常不重试,默认为空
     * @return result
     * @throws CustomRetryException 指定异常
     */
    @Retryable(value = {CustomRetryException.class},
        maxAttempts = 4,backoff = @Backoff(200))
    String retry() throws CustomRetryException;
}

@Retryable注解参数说明:

maxAttempts :最大重试次数,默认为3,如果要设置的重试次数为3,可以不写;

value:抛出指定异常才会重试,支持多异常

include:和value一样,默认为空,当exclude也为空时,默认所以异常

exclude:指定不处理的异常

backoff:重试等待时间策略,默认使用@Backoff的value默认为1000L,我们设置为200L。

@Backoff注解中的参数说明:

value:隔多少毫秒后重试,默认为1000L;

delay:和value一样,但是默认为0;

multiplier(指定延迟倍数)默认为0,表示固定暂停1秒后进行重试,如果把multiplier设置为1.5,则第一次重试为2秒,第二次为3秒,第三次为4.5秒。

④可以同时在同一个类中使用@Recover来处理N次处理后都没有成功后需要处理的事情

可以在指定方法上标记@Recover来开启重试失败后调用的方法(注意,需跟重处理方法在同一个类中)

3e6b29cabf1d44328c04240da2b9e781.png

2、请求降级

使用@Recover实现降级措施

当重试到达指定次数时,被注解的方法将被回调,可以在该方法中进行日志处理。需要注意的是发生的异常和入参类型一致时才会回调。

@Retryable和@Recover修饰的方法要在同一个类中,且被@Retryable 标记的方法不能有返回值,这样Recover方法才会生效。

/**
     * value:抛出指定异常才会重试
     * include:和value一样,默认为空,当exclude也为空时,默认所有异常
     * exclude:指定不处理的异常
     * maxAttempts:最大重试次数,默认3次
     * backoff:重试等待策略,
     * 默认使用@Backoff,@Backoff的value默认为1000L,我们设置为2000; 以毫秒为单位的延迟(默认 1000)
     * multiplier(指定延迟倍数)默认为0,表示固定暂停1秒后进行重试,如果把multiplier设置为1.5,则第一次重试为2秒,第二次为3秒,第三次为4.5秒。
     * @param code
     * @return
     * @throws Exception
     */
    @Override
    @Retryable(value = Exception.class,maxAttempts = 3,backoff = @Backoff(delay = 2000,multiplier = 1.5))
    public int testRetry(int code) throws Exception{
        System.out.println("test被调用,时间:"+ LocalTime.now());
          if (code==0){
              throw new Exception("情况不对头!");
          }
        System.out.println("test被调用,情况对头了!");
 
        return 200;
    } 

/**
     * Spring-Retry还提供了@Recover注解,用于@Retryable重试失败后处理方法。
     * 如果不需要回调方法,可以直接不写回调方法,那么实现的效果是,重试次数完了后,如果还是没成功没符合业务判断,就抛出异常。
     * 可以看到传参里面写的是 Exception e,这个是作为回调的接头暗号(重试次数用完了,还是失败,我们抛出这个Exception e通知触发这个回调方法)。
     * 注意事项:
     * 方法的返回值必须与@Retryable方法一致
     * 方法的第一个参数,必须是Throwable类型的,建议是与@Retryable配置的异常一致,其他的参数,需要哪个参数,写进去就可以了(@Recover方法中有的)
     * 该回调方法与重试方法写在同一个实现类里面
     *
     * 由于是基于AOP实现,所以不支持类里自调用方法
     * 如果重试失败需要给@Recover注解的方法做后续处理,那这个重试的方法不能有返回值,只能是void
     * 方法内不能使用try catch,只能往外抛异常
     * @Recover注解来开启重试失败后调用的方法(注意,需跟重处理方法在同一个类中),此注解注释的方法参数一定要是@Retryable抛出的异常,否则无法识别,可以在该方法中进行日志处理。
     * @param e
     * @param code
     * @return
     */
    @Recover
    public int recover(Exception e, int code){
        System.out.println("回调方法执行!!!!");
        //记日志到数据库 或者调用其余的方法
        System.out.println("异常信息:"+e.getMessage());
        return 400;
    } 

3、 RetryTemplate

对每个方法上进行注解定义以及对应降低方法定义,过于繁琐。

spring 提供 retryTemplate 的bean对象,定义一个可重试、降级的代理对象。

RetryTemplate提供了RetryOperations的一种具体实现。它被认为是从中创建bean的良好做法。

1、定义retryTemplate对象

    @Bean
    @ConditionalOnMissingBean
    public RetryTemplate retryTemplate(){
        final SimpleRetryPolicy simpleRetryPolicy = new SimpleRetryPolicy();
        simpleRetryPolicy.setMaxAttempts(4);
​
        final FixedBackOffPolicy fixedBackOffPolicy = new FixedBackOffPolicy();
        fixedBackOffPolicy.setBackOffPeriod(1000L);
​
        return RetryTemplate.builder()
            .customPolicy(simpleRetryPolicy)
            .customBackoff(fixedBackOffPolicy)
            .retryOn(CustomRetryException.class)
            .build();
    }

 2、使用retryTemplate

@Autowired
    pivate RetryTemplate retryTemplate;
​
    @Test
    void retryWithoutAnnotation(){
        try {
            String message = retryTemplate.execute(x -> retryService.retryWithoutAnnotation());
            log.info("message = "+message);
        } catch (CustomRetryException e) {
            log.error("Error while executing test {}",e.getMessage());
        }
    }

3、RecoveryCallback 降级

execute时,可以选择输入RecoveryCallback回调,确定重试结束后,仍然出现异常的recovery行为。自定义方法如下:

@Slf4j
public class CustomRecoveryCallback implements RecoveryCallback<String> {
​
    @Override
    public String recover(RetryContext retryContext) throws Exception {
        log.info("Default Retry service test,total retry {}",retryContext.getRetryCount());
        return "Error Class :: " + retryContext.getLastThrowable().getClass().getName();
    }
}

4、RetryListenerSupport生命周期控制

如果我们想在重试整个生命周期中,按照不同的阶段设置一些事件监听处理机制,那怎么办呢?设置自定义的RetryListenerSupport能帮助到我们。我们继承RetryListenerSupport,并重新Override close 、onError、open方法,这三个方法分别表示

  • 所有重试结束时 close
  • 每一次重试发生异常时 onError
  • 重试正式开始前 open
@Slf4j
public class DefaultListenerSupport extends RetryListenerSupport {
​
    @Override
    public <T, E extends Throwable> void close(RetryContext context, RetryCallback<T, E> callback, Throwable throwable) {
        log.info("DefaultListenerSupport close");
        super.close(context, callback, throwable);
    }
​
    @Override
    public <T, E extends Throwable> void onError(RetryContext context, RetryCallback<T, E> callback, Throwable throwable) {
        log.info("DefaultListenerSupport onError");
        super.onError(context, callback, throwable);
    }
​
    @Override
    public <T, E extends Throwable> boolean open(RetryContext context, RetryCallback<T, E> callback) {
        log.info("DefaultListenerSupport open");
        return super.open(context, callback);
    }
}

 并且在构造RetryTemaplate时候,设置withListener字段。

@Bean
@ConditionalOnMissingBean
public RetryListenerSupport retryListenerSupport(){
    return new DefaultListenerSupport();
}
​
@Bean
@ConditionalOnMissingBean
public RetryTemplate retryTemplate(RetryListenerSupport retryListenerSupport){
​
    ...
    
    return RetryTemplate.builder()
        .customPolicy(simpleRetryPolicy)
        .customBackoff(fixedBackOffPolicy)
        .withListener(retryListenerSupport)
        .retryOn(CustomRetryException.class)
        .build();
}

 

 

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

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

相关文章

无人机应用场景和发展趋势,无人机技术的未来发展趋势分析

随着科技的不断发展&#xff0c;无人机技术也逐渐走进了人们的生活和工作中。无人机被广泛应用于很多领域&#xff0c;例如遥感、民用、军事等等。本文将围绕无人机技术的应用场景和发展趋势&#xff0c;从多角度展开分析。 无人机技术的应用场景 无人机在遥感方面的应用&…

2024 年,如何用 6 个月拿下 30 万年薪的区块链开发?

《区块链简易速速上手小册》开源系列专栏 文章目录 一、了解区块链二、简易速速上手小册系列&#xff08;开源版&#xff09;三、具体安排参考第1个月&#xff1a;基础加固第2个月&#xff1a;区块链原理与技术第3个月&#xff1a;深入智能合约开发第4个月&#xff1a;区块链平…

vtkActor 设置特定图层 显示及置顶显示

问题&#xff0c;有时我们需要显示某个 Actor 在相机最前面&#xff0c;可以遮盖后面的物体;显示在顶层有点不准确&#xff1b;因为这个还相机位置也有关系&#xff1b; 这里讲三种情况&#xff1a; 1. 设置 Mapper 顶层&#xff0c;尝试了一下&#xff0c;可以用于某些场景&…

C++ 模板初阶【函数模板,类模板】

文章目录 泛型编程函数模板概念函数模板的格式函数模板的原理函数模板的实例化隐式实例化:让编译器根据实参推演模板参数的实际类型显式实例化&#xff1a;在函数名后的<>中指定模板参数的实际类型 函数模板的匹配规则 类模板概念类模板格式类模板的实例化 泛型编程 在我…

【计算几何】确定两条连续线段向左转还是向右转

确定两条连续线段向左转还是向右转 目录 一、说明二、算法2.1 两点的叉积2.2 两个段的叉积 三、旋转方向判别3.1 左转3.2、右转3.3 共线判别 一、说明 如果是作图&#xff0c;或者是判别小车轨迹。为了直观地了解&#xff0c;从当前点到下一个点过程中&#xff0c;什么是左转、…

2-1 动手学深度学习v2-Softmax回归-笔记

回归 VS 分类 回归估计一个连续值分类预测一个离散类别 从回归到多类分类 回归 单连续数值输出输出的区间&#xff1a;自然区间 R \mathbb{R} R损失&#xff1a;跟真实值的区别 分类 通常多个输出&#xff08;这个输出的个数是等于类别的个数&#xff09;输出的第 i i i…

Redis核心技术与实战【学习笔记】 - 27.限制Redis Cluster规模的因素(通信开销)

简述 Redis Cluster 能保存的数据量以及支撑的吞吐量&#xff0c;跟集群实例规模相关。 Redis 官方给出了 Redis Cluster 的规模上线&#xff0c;就是一个集群运行 1000 个实例。 其实&#xff0c;限定 Redis Cluster 集群规模的一个关键因素就是&#xff0c;实例间的通信开销…

MySQL优化器

优化器 MySQL存储引擎中存在了一个可插拔的优化器OPTIMIZER_TRACE&#xff0c;可以看到内部查询计划的TRACE信息&#xff0c;从而可以知道MySQL内部执行过程 查询优化器状态 show variables like optimizer_trace;Variable_name Valueoptimizer_trace enabledoff,one_lineoff…

大模型实战营第二期——3. 基于 InternLM 和 LangChain 搭建你的知识库

github地址&#xff1a;InternLM/tutorial-书生浦语大模型实战营文档地址&#xff1a;基于 InternLM 和 LangChain 搭建你的知识库视频地址&#xff1a;基于 InternLM 和 LangChain 搭建你的知识库Intern Studio: https://studio.intern-ai.org.cn/console/instance动手学大模型…

如何在苹果Mac上进行分屏,多任务处理?

Apple 在 macOS Catalina 中引入了 Split View&#xff0c;让您可以同时查看两个应用程序。如果同时处理多个应用程序&#xff0c;但在它们之间切换时感到沮丧&#xff0c;小编教给大家在 Macbook Pro/Air 或 iMac 上使用分屏功能流畅地进行多任务处理。 注意&#xff1a;您可…

C# 委托(delegate)本质理解

目录 代码如下&#xff0c;很简单 运行的结果 反编译程序查看 关注两点&#xff1a; 什么是委托 委托的三个步骤 委托的意义 代码如下&#xff0c;很简单 namespace Delegate { class Program { delegate void SayHi(); void SayHi_1() …

专业135+总400+中国科学院大学859国科大信号与系统考研经验电子信息与通信,真题,大纲,参考书

今年考研专业课859信号与系统135&#xff0c;总分400上岸国科大&#xff0c;总结一下自己这一年的复习经验&#xff0c;希望对后面报考中科院大学的同学有所帮助。 专业课&#xff1a; 国科大不同研究所都是统一命题&#xff0c;859信号与系统的参考书目是郑君里的《信号与系…

移动光猫gs3101超级密码及改桥接模式教程

文章目录 超级管理员账号改桥接模式路由器连接光猫&#xff0c;PPPOE拨号即可&#xff01;附录&#xff1a;如果需要改桥接的话不知道拨号密码咋办打开光猫Telnet功能Telnet 登录 参考文章 移动光猫吉比特GS3101超级账号获取更改桥接 移动光猫gs3101超级密码及改桥接模式教程 …

C#入门及进阶|数组和集合(六):集合概述

1.集合概述 数组是一组具有相同名称和类型的变量集合&#xff0c;但是数组初始化后就不便于再改变其大小&#xff0c;不能实现在程序中动态添加和删除数组元素&#xff0c;使数组的使用具有很多局限性。集合能解决数组存在的这个问题&#xff0c;下面我们来学习介绍集合…

微服务入门篇:http客户端Feign(远程调用,自定义配置,Feign的性能优化,Feign服务抽取)

目录 1.基于Feign的远程调用1.RestTemplate方式调用存在的问题2.Feign的介绍3.定义和使用Feign客户端 2.自定义配置1.方式一&#xff1a;配置文件方式2.方式二: java代码方式&#xff0c;需要先声明一个Bean: 3.Feign的性能优化1.Feign底层的客户端实现2.连接池配置 4.Feign的最…

春节假期:思考新一年的发展思路

春节假期是人们放松身心、享受家庭团聚的时刻&#xff0c;但除了走亲戚、玩、吃之外&#xff0c;我们确实也需要思考新的一年的发展思路。以下是一些建议&#xff0c;帮助您在春节假期中为新的一年做好准备&#xff1a; 回顾过去&#xff0c;总结经验&#xff1a;在春节期间&a…

Blazor 子组件交互例子

源码 子组件 SwitchBar.razor &#xfeff;using Microsoft.Extensions.Logging inject ILogger<Index> Logger<div style"ClassString" onclick"OnClick">ChildContent </div>code {[Parameter]public RenderFragment? ChildContent…

AJAX——认识URL

1 什么是URL&#xff1f; 统一资源定位符&#xff08;英语&#xff1a;Uniform Resource Locator&#xff0c;缩写&#xff1a;URL&#xff0c;或称统一资源定位器、定位地址、URL地址&#xff09;俗称网页地址&#xff0c;简称网址&#xff0c;是因特网上标准的资源的地址&…

LeetCode---383周赛

题目列表 3028. 边界上的蚂蚁 3029. 将单词恢复初始状态所需的最短时间 I 3030. 找出网格的区域平均强度 3031. 将单词恢复初始状态所需的最短时间 II 一、边界上的蚂蚁 这题没什么好说的&#xff0c;模拟就行&#xff0c;本质就是看前缀和有几个为0。 代码如下 class S…

【制作100个unity游戏之25】3D背包、库存、制作、快捷栏、存储系统、砍伐树木获取资源、随机战利品宝箱1(附带项目源码)

效果演示 文章目录 效果演示系列目录前言人物和视角基本控制简单的背包系统和物品交互绘制背包UI脚本控制 源码完结 系列目录 前言 欢迎来到【制作100个Unity游戏】系列&#xff01;本系列将引导您一步步学习如何使用Unity开发各种类型的游戏。在这第25篇中&#xff0c;我们将…