springboot系列——重试机制原理和应用,还有比这个讲的更好的吗(附完整源码)

简介:

springboot系列——重试机制原理和应用,还有比这个讲的更好的吗(附完整源码)

  1. 理解重试机制
  2. 总结重试机制使用场景
  3. spring-retry重试组件
  4. 手写一个基于注解的重试组件
  5. 重试机制下会出现的问题
  6. 模板方法设计模式实现异步重试机制
    如果有,请转给我!
  7. 理解重试机制

“重试是为了提高成功的可能性“

反过来理解,任何可能失败且允许重试操作的场景,就适合使用重试机制。但有了重试机制就一定能成功吗?显然不是。如果不成功就一直重试,这种处理方式会使得业务线程一直被重试占用,这样会导致服务的负载线程暴增直至服务宕机,因此需要限制重试次数。失败情况下,我们需要做后续的操作,如果是数据库操作的重试,需要回滚事物;如果是服务调用的重试,需要邮件报警通知运维开发人员,恢复服务。

对于服务接口调用,可能是因为网络波动导致超时失败,这时候所有重试次数是在很短时间内发起的话,就很容易全部超时失败,因此超时机制还需要引入重试动作之间时间间隔以及第一次失败后延迟多长时间再开始重试等机制。

重试机制要素

限制重试次数
每次重试的时间间隔
最终失败结果的报警或事物回滚
在特定失败异常事件情况下选择重试

  1. 总结重试机制使用场景

任何可能失败且允许重试操作的场景,就适合使用重试机制。那么在分布式系统开发环境中,哪些场景需要是使用重试机制呢。

乐观锁机制保证数据安全的数据更新场景,如账户信息的金额数据更新。
微服务的分布式架构下,服务的调用因超时而失败。

  1. spring-retry重试组件

spring-retry核心:配置重试元数据,失败恢复或报警通知。

pom文件依赖

<groupId>org.springframework.retry</groupId>
<artifactId>spring-retry</artifactId>


配置重试元数据

@Override
@Retryable(value = Exception.class,maxAttempts = 3 , backoff = @Backoff(delay = 2000,multiplier = 1.5))
public int retryServiceOne(int code) throws Exception {

// TODO Auto-generated method stub 
System.out.println("retryServiceOne被调用,时间:"+LocalTime.now());
System.out.println("执行当前业务逻辑的线程名:"+Thread.currentThread().getName());
if (code==0){
    throw new Exception("业务执行失败情况!");
}
System.out.println("retryServiceOne执行成功!");

return 200;

}

配置元数据情况:

重试次数为3
第一次重试延迟2s
每次重试时间间隔是前一次1.5倍
Exception类异常情况下重试
测试:

启动应用,浏览器输入:http://localhost:8080/springRetry

后台结果:

执行业务发起逻辑的线程名:http-nio-8080-exec-6
retryServiceOne被调用,时间:17:55:48.235
执行当前业务逻辑的线程名:http-nio-8080-exec-6
retryServiceOne被调用,时间:17:55:50.235
执行当前业务逻辑的线程名:http-nio-8080-exec-6
retryServiceOne被调用,时间:17:55:53.236
执行当前业务逻辑的线程名:http-nio-8080-exec-6
回调方法执行!!!!

  1. 手写一个基于注解的重试组件

注解类:

/**

  • 重试注解
    */

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
@Documented
public @interface JdkRetry{


//默认
 int maxAttempts() default 3;
//默认每次间隔等待3000毫秒
 long waitTime() default 3000;

//捕捉到的异常类型  再进行重发
 Class<?> exception () default Exception.class ;

 String recoverServiceName () default "DefaultRecoverImpl";

}

注解类包含的元数据有:

尝试次数
重试间隔时间
抛出哪种异常会重试
重试完后还是失败的恢复类
使用spring AOP技术,实现重试注解的切面逻辑类RetryAspect。

@Transactional(rollbackFor = Exception.class)

  @Around("@annotation(jdkRetry)")
//开发自定义注解的时候,定要注意  @annotation(jdkRetry)和下面方法的参数,按规定是固定的形式的,否则报错
  public Object doConcurrentOperation(ProceedingJoinPoint pjp , JdkRetry jdkRetry) throws Throwable {
    //获取注解的属性

// pjp.getClass().getMethod(, parameterTypes)

    System.out.println("切面作用:"+jdkRetry.maxAttempts()+ "  恢复策略类:"+ jdkRetry.recoverServiceName());
    
    Object service = JdkApplicationContext.jdkApplicationContext.getBean(jdkRetry.recoverServiceName());
    Recover recover = null;
    if(service == null)
        return new Exception("recover处理服务实例不存在");
    recover = (Recover)service;
    
    long waitTime =  jdkRetry.waitTime();
    maxRetries = jdkRetry.maxAttempts();
    Class<?> exceptionClass = jdkRetry.exception();
    
    
    int numAttempts = 0;
    do {
        numAttempts++;
        try {
            //再次执行业务代码
            return pjp.proceed();
        } catch (Exception ex) {
            //必须只是乐观锁更新才能进行重试逻辑
            System.out.println(ex.getClass().getName());
            if(!ex.getClass().getName().equals(exceptionClass.getName()))
                throw ex;
            if (numAttempts > maxRetries) {
                
                recover.recover(null);
                //log failure information, and throw exception

// 如果大于 默认的重试机制 次数,我们这回就真正的抛出去了
// throw new Exception("重试逻辑执行完成,业务还是失败!");

            }else{
                //如果 没达到最大的重试次数,将再次执行
                System.out.println("=====正在重试====="+numAttempts+"次");
                TimeUnit.MILLISECONDS.sleep(waitTime);
            }
        }
    } while (numAttempts <= this.maxRetries);

    return 500;
  }

切面类获取到重试注解元信息后,切面逻辑会做以下相应的处理:

捕捉异常,对比该异常是否应该重试
统计重试次数,判断是否超限
重试多次后失败,执行失败恢复逻辑或报警通知
测试:

启动应用,浏览器输入:http://localhost:8080/testAnnotationRetry

结果:

切面作用:3 恢复策略类:DefaultRecoverImpl
AnnotationServiceImpl被调用,时间:18:11:25.748
org.jackdking.retry.jdkdkingannotation.retryException.UpdateRetryException
=====正在重试=====1次
AnnotationServiceImpl被调用,时间:18:11:28.748
org.jackdking.retry.jdkdkingannotation.retryException.UpdateRetryException
=====正在重试=====2次
AnnotationServiceImpl被调用,时间:18:11:31.749
org.jackdking.retry.jdkdkingannotation.retryException.UpdateRetryException
=====正在重试=====3次
AnnotationServiceImpl被调用,时间:18:11:34.749
org.jackdking.retry.jdkdkingannotation.retryException.UpdateRetryException
2020-05-26 18:11:34.749 ERROR 14892 --- [io-8080-exec-10] o.j.r.j.recover.impl.DefaultRecoverImpl : 重试失败,未进行任何补全,此为默认补全:打出错误日志

  1. 重试机制下会出现的问题

幂等性问题:

在分布式架构下,服务之间调用会因为网络原因出现超时失败情况,而重试机制会重复多次调用服务,但是对于被调用放,就可能收到了多次调用。如果被调用方不具有天生的幂等性,那就需要增加服务调用的判重模块,并对每次调用都添加一个唯一的id。

大量请求超时堆积:

超高并发下,大量的请求如果都进行超时重试的话,如果你的重试时间设置不安全的话,会导致大量的请求占用服务器线程进行重试,这时候服务器线程负载就会暴增,导致服务器宕机。对于这种超高并发下的重试设计,我们不能让重试放在业务线程,而是统一由异步任务来执行。

  1. 模板方法设计模式实现异步重试机制

模板方法设计模式来实现异步重试机制

所有业务类继承重试模板类RetryTemplate

@Service("serviceone")
public class RetryTemplateImpl extends RetryTemplate{


public RetryTemplateImpl() {
    // TODO Auto-generated constructor stub
    this.setRecover(new RecoverImpl());
}

@Override
protected Object doBiz() throws Exception {
    // TODO Auto-generated method stub
    int code = 0;
    System.out.println("RetryTemplateImpl被调用,时间:"+LocalTime.now());
    if (code==0){
        throw new Exception("业务执行失败情况!");
    }
    System.out.println("RetryTemplateImpl执行成功!");

    return 200;
}

class RecoverImpl implements Recover{

    @Override
    public String recover() {
        // TODO Auto-generated method stub
        System.out.println("重试失败   恢复逻辑,记录日志等操作");
        return null;
    }
}

}

业务实现类在doBiz方法内实现业务过程
所有业务实现一个恢复类,实现Recover接口,重试多次失败后执行恢复逻辑
测试:

启动应用,浏览器输入:http://localhost:8080/testRetryTemplate

结果:

2020-05-26 22:53:41.935 INFO 25208 --- [nio-8080-exec-4] o.j.r.r.c.RetryTemplateController : 开始执行业务
RetryTemplateImpl被调用,时间:22:53:41.936
RetryTemplateImpl被调用,时间:22:53:41.938
RetryTemplateImpl被调用,时间:22:53:44.939
RetryTemplateImpl被调用,时间:22:53:47.939
2020-05-26 22:53:50.940 INFO 25208 --- [pool-1-thread-1] o.j.r.r.service.RetryTemplate : 业务逻辑失败,重试结束
重试失败 恢复逻辑,记录日志等操作

完整的demo项目,请关注公众号“前沿科技bot“并发送"重试机制"获取。

原文地址https://www.cnblogs.com/HelloWorld2016425/p/12984737.html

相关文章
|
2月前
|
Java Spring 容器
SpringBoot自动配置的原理是什么?
Spring Boot自动配置核心在于@EnableAutoConfiguration注解,它通过@Import导入配置选择器,加载META-INF/spring.factories中定义的自动配置类。这些类根据@Conditional系列注解判断是否生效。但Spring Boot 3.0后已弃用spring.factories,改用新格式的.imports文件进行配置。
725 0
|
4月前
|
JavaScript 前端开发 Java
制造业ERP源码,工厂ERP管理系统,前端框架:Vue,后端框架:SpringBoot
这是一套基于SpringBoot+Vue技术栈开发的ERP企业管理系统,采用Java语言与vscode工具。系统涵盖采购/销售、出入库、生产、品质管理等功能,整合客户与供应商数据,支持在线协同和业务全流程管控。同时提供主数据管理、权限控制、工作流审批、报表自定义及打印、在线报表开发和自定义表单功能,助力企业实现高效自动化管理,并通过UniAPP实现移动端支持,满足多场景应用需求。
421 1
|
5月前
|
前端开发 Java 关系型数据库
基于Java+Springboot+Vue开发的鲜花商城管理系统源码+运行
基于Java+Springboot+Vue开发的鲜花商城管理系统(前后端分离),这是一项为大学生课程设计作业而开发的项目。该系统旨在帮助大学生学习并掌握Java编程技能,同时锻炼他们的项目设计与开发能力。通过学习基于Java的鲜花商城管理系统项目,大学生可以在实践中学习和提升自己的能力,为以后的职业发展打下坚实基础。技术学习共同进步
408 7
|
2月前
|
前端开发 Java 数据库连接
SpringBoot参数校验底层原理和实操。深度历险、深度解析(图解+秒懂+史上最全)
SpringBoot参数校验底层原理和实操。深度历险、深度解析(图解+秒懂+史上最全)
SpringBoot参数校验底层原理和实操。深度历险、深度解析(图解+秒懂+史上最全)
|
5月前
|
前端开发 Java 物联网
智慧班牌源码,采用Java + Spring Boot后端框架,搭配Vue2前端技术,支持SaaS云部署
智慧班牌系统是一款基于信息化与物联网技术的校园管理工具,集成电子屏显示、人脸识别及数据交互功能,实现班级信息展示、智能考勤与家校互通。系统采用Java + Spring Boot后端框架,搭配Vue2前端技术,支持SaaS云部署与私有化定制。核心功能涵盖信息发布、考勤管理、教务处理及数据分析,助力校园文化建设与教学优化。其综合性和可扩展性有效打破数据孤岛,提升交互体验并降低管理成本,适用于日常教学、考试管理和应急场景,为智慧校园建设提供全面解决方案。
375 70
|
3月前
|
SQL Java 数据库
解决Java Spring Boot应用中MyBatis-Plus查询问题的策略。
保持技能更新是侦探的重要素质。定期回顾最佳实践和新技术。比如,定期查看MyBatis-Plus的更新和社区的最佳做法,这样才能不断提升查询效率和性能。
148 1
|
4月前
|
供应链 JavaScript BI
ERP系统源码,基于SpringBoot+Vue+ElementUI+UniAPP开发
这是一款专为小微企业打造的 SaaS ERP 管理系统,基于 SpringBoot+Vue+ElementUI+UniAPP 技术栈开发,帮助企业轻松上云。系统覆盖进销存、采购、销售、生产、财务、品质、OA 办公及 CRM 等核心功能,业务流程清晰且操作简便。支持二次开发与商用,提供自定义界面、审批流配置及灵活报表设计,助力企业高效管理与数字化转型。
428 2
ERP系统源码,基于SpringBoot+Vue+ElementUI+UniAPP开发
|
4月前
|
安全 Java API
Spring Boot 功能模块全解析:构建现代Java应用的技术图谱
Spring Boot不是一个单一的工具,而是一个由众多功能模块组成的生态系统。这些模块可以根据应用需求灵活组合,构建从简单的REST API到复杂的微服务系统,再到现代的AI驱动应用。
|
3月前
|
机器学习/深度学习 数据采集 人机交互
springboot+redis互联网医院智能导诊系统源码,基于医疗大模型、知识图谱、人机交互方式实现
智能导诊系统基于医疗大模型、知识图谱与人机交互技术,解决患者“知症不知病”“挂错号”等问题。通过多模态交互(语音、文字、图片等)收集病情信息,结合医学知识图谱和深度推理,实现精准的科室推荐和分级诊疗引导。系统支持基于规则模板和数据模型两种开发原理:前者依赖人工设定症状-科室规则,后者通过机器学习或深度学习分析问诊数据。其特点包括快速病情收集、智能病症关联推理、最佳就医推荐、分级导流以及与院内平台联动,提升患者就诊效率和服务体验。技术架构采用 SpringBoot+Redis+MyBatis Plus+MySQL+RocketMQ,确保高效稳定运行。
236 0
|
6月前
|
小程序 Java 关系型数据库
weixin117新闻资讯系统设计+springboot(文档+源码)_kaic
本文介绍了一款基于微信小程序的新闻资讯系统,涵盖其开发全过程。该系统采用Java的SSM框架进行后台管理开发,使用MySQL作为本地数据库,并借助微信开发者工具确保稳定性。管理员可通过个人中心、用户管理等功能模块实现高效管理,而用户则能注册登录并查看新闻与视频内容。系统设计注重可行性分析(技术、经济、操作),强调安全性与数据完整性,界面简洁易用,功能全面,极大提升了信息管理效率及用户体验。关键词包括基于微信小程序的新闻资讯系统、SSM框架和MYSQL数据库。