如果有,请转给我!

1. 理解重试机制

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

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

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

重试机制要素

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

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

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

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

3. spring-retry重试组件

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

pom文件依赖

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

配置重试元数据

@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
回调方法执行!!!!

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

注解类:

/**
* 重试注解
*/
@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 : 重试失败,未进行任何补全,此为默认补全:打出错误日志

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

幂等性问题:

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

大量请求超时堆积:

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

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

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

所有业务类继承重试模板类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“并发送"重试机制"获取。

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

  1. 基于spring-boot和docker-java实现对docker容器的动态管理和监控[附完整源码下载]

    ​ (我是个封面) docker简介 Docker 是一个开源的应用容器引擎,和传统的虚拟机技术相比,Docker 容器性能开销极低,因此也广受开发者喜爱.随着基于docker的开发者越来越多,doc ...

  2. SpringBoot系列——状态机(附完整源码)

    1. 简单介绍状态机 2. 状态机的本质 3. 状态机应用场景 1. 简单介绍状态机 状态机由状态寄存器和组合逻辑电路构成,能够根据控制信号按照预先设定的状态进行状态转移,是协调相关信号动作.完成特定 ...

  3. (二、下) springBoot 、maven 、mysql、 mybatis、 通用Mapper、lombok 简单搭建例子 《附项目源码》

    接着上篇文章中 继续前进. 一.在maven 的pom.xm中添加组件依赖, mybatis通用Mapper,及分页插件 1.mybatis通用Mapper <!-- mybatis通用Mapp ...

  4. Shiro整合springboot,freemaker,redis(含权限系统完整源码)

    区块链技术联盟 2018-02-08 17:06:40 目录 一.导语 二.shiro功能介绍 三.shiro详解 四.shiro实战案例分享 五.系统配置 六.其他 一.导语 今天推荐给大家一个非常 ...

  5. 【陪你系列】5 千字长文+ 30 张图解 | 陪你手撕 STL 空间配置器源码

    大家好,我是小贺. 点赞再看,养成习惯 文章每周持续更新,可以微信搜索「herongwei」第一时间阅读和催更,本文 GitHub https://github.com/rongweihe/MoreT ...

  6. 鸿蒙内核源码分析(调度机制篇) | 任务是如何被调度执行的 | 百篇博客分析OpenHarmony源码 | v7.07

    百篇博客系列篇.本篇为: v07.xx 鸿蒙内核源码分析(调度机制篇) | 任务是如何被调度执行的 | 51.c.h .o 任务管理相关篇为: v03.xx 鸿蒙内核源码分析(时钟任务篇) | 触发调 ...

  7. JUC.Lock(锁机制)学习笔记[附详细源码解析]

    锁机制学习笔记 目录: CAS的意义 锁的一些基本原理 ReentrantLock的相关代码结构 两个重要的状态 I.AQS的state(int类型,32位) II.Node的waitStatus 获 ...

  8. [Android实例] Scroll原理-附ScrollView源码分析

    想象一下你拿着放大镜贴很近的看一副巨大的清明上河图, 那放大镜里可以看到的内容是很有限的, 而随着放大镜的上下左右移动,就可以看到不同的内容了 android中手机屏幕就相当于这个放大镜, 而看到的内 ...

  9. springboot aop 自定义注解方式实现一套完善的日志记录(完整源码)

    https://www.cnblogs.com/wenjunwei/p/9639909.html https://blog.csdn.net/tyrant_800/article/details/78 ...

随机推荐

  1. Codeforce 1155D Beautiful Array(DP)

    D. Beautiful Array You are given an array aa consisting of nn integers. Beauty of array is the maxim ...

  2. POJ 1287 Networking 垃圾题目

    Networking Time Limit: 1000MS   Memory Limit: 10000K Total Submissions: 22362   Accepted: 11372 Desc ...

  3. IBM Rational Rose软件下载以及全破解方法

    最近忙着作业,软件设计的类图着实难画,于是整理了rose的下载和破解方法 Rational Rose是Rational公司出品的一种面向对象的统一建模语言的可视化建模工具.用于可视化建模和公司级水平软 ...

  4. 【mybatis】IF判断的坑

    http://cheng-xinwei.iteye.com/blog/2008200 最近在项目使用mybatis中碰到个问题 <if test="type=='y'"> ...

  5. apache反向代理和负载均衡

    正向代理:正如我们用的游戏加速代理,大多的个人PC把请求发给正向代理服务器,代理服务器通常配置高端的带宽,替我们请求相应的服务 负载均衡中的反向代理:通常意义上,是一个请求转发的代理.类似一个收发室的 ...

  6. 【Spark】通过SparkStreaming实现从socket接受数据,并进行简单的单词计数

    文章目录 步骤 一.创建maven工程并导入jar包 二.安装并启动生产者 三.开发SparkStreaming代码 四.查看结果 步骤 一.创建maven工程并导入jar包 <properti ...

  7. Android Bluetooth How To--Based on Android L Bluedroid

    Android Bluetooth How To(Based on Android L Bluedroid) 持续更新中… 1.How to enable btsnoop log? a) UI Set ...

  8. css实现双色饼图

    from:wx--前端早读课 首先回想用css画三角形的方法: <div class="triangle"></div> .triangle { displ ...

  9. removebg抠图小工具

    由于比较简单,直接上代码(removebg接口官网),更多小工具获取 (1)官网API,需注册获取X-Api-Key:removebg_官网api.py import requests respons ...

  10. maven开发SSH

    虽然开发SSH的基本步骤都差不多,但每次都从头开始做真的会有点儿烦,把maven的SSH框的基本代码放出来,下次就可以复制粘贴哈哈. 1. 配置文件: (1)pom.xml <project x ...