前言:

在上一篇文章 用rabbitmq实现消息重发功能 中,使用了外部的rabbitmq来实现了消息重发的功能,但是使用rabbitmq来实现并不适用于所有的场景,在这篇文章中,我再扩展两种仅用java本身就能实现的方法。

  • Retryer
  • @Retryable

实现过程:

Retryer

Retryer是谷歌 Guava 库下的工具,用 Retryer可以封装成util类,只需要几行代码,就能完成一个很简单的重试功能了。

首先是导包

<dependency><groupId>com.github.rholder</groupId><artifactId>guava-retrying</artifactId><version>2.0.0</version>
</dependency>

Mavn库中,目前最新的就是2.0.0这个版本了 。

接下来就是工具类的实现了


import com.github.rholder.retry.*;
import com.google.common.base.Predicate;import java.util.concurrent.TimeUnit;/*** guava重试工具*/
public class RetryUtil {/*** 对使用的方法进行重试,与预期不一样的结果也可以进行重试** @param attemptNumber      执行次数 1是只执行1次,相当于不重试* @param retryInterval      每次重试的时间间隔,单位:秒* @param predicateResult    结果谓词,如果谓词不为null,并且谓词条件满足,也会进行重试的。条件不满足的情况下则不进行重试。(可选* @param predicateException 异常谓词,不为null则会匹配传入的异常,满足条件,则会重试。如果为null,则全部异常都会进行重试* @param <R>                返回类型* @return* @throws RetryException     执行完全不的重试次数后,还是未成功,那么会抛出异常,建议外层调用是catch住*/public static <R> Retryer<R> buildRetryer(int attemptNumber, long retryInterval, Predicate<R> predicateResult, Predicate<Throwable> predicateException) {// 构建重试器,包括重试次数,间隔时间等,并指定返回类型RetryerBuilder<R> retryerBuilder = RetryerBuilder.<R>newBuilder()// 设置重试次数.withStopStrategy(StopStrategies.stopAfterAttempt(attemptNumber))// 重试间隔时间.withWaitStrategy(WaitStrategies.fixedWait(retryInterval, TimeUnit.SECONDS));// 重试匹配的谓词if (predicateResult != null) {retryerBuilder.retryIfResult(predicateResult);}// 重试可匹配的异常if (predicateException != null) {retryerBuilder.retryIfException(predicateException);} else {retryerBuilder.retryIfException();}// 进行建造Retryer<R> retryer = retryerBuilder.build();return retryer;}
}

很明显Retryer是使用了建造者模式,我们在util中分别对重试次数,重试时间,以及匹配的谓词和匹配的异常进行了配置,最后得到一个符合我们使用的Retryer对象。

下面是我使用时的一个简单的例子

@Controller
@RequestMapping("test")
public class Test {@RequestMapping("/ttt")public void Test(){Gson gson = new Gson();// 最大重试10次,每次间隔2sRetryer<BaseResp> retryer = RetryUtil.buildRetryer(10,2,(t) -> "FAILD".equals(t.getIfSuccess()),null);try {BaseResp baseResp = retryer.call(() -> {BaseResp bb = getResult();return bb;});System.out.println(" baseResp " + gson.toJson(baseResp));}catch (Exception e){e.printStackTrace();}}private BaseResp getResult(){log.err("运行result方法");return BaseResp.faild("FAILD");}}

使用retryer的call方法,在call中写入我们要重复调用的方法,成功后,通过return出来相同的对象,我们可以直接使用。

getResult 这个方法写死返回 FAILD,我们的结果谓词中,每次执行时,equals的结构都会是true,这个时候就会接着重复执行下去。如果getResult返回了SUCCESS,那么重试就结束了。

getResult如果抛出来了异常,那么还是会触发重试的,因为我们在异常谓词中配的null,也就是全部异常都要重试。

在全部重试次数执行结束后,仍然没有返回理想的结果,是会抛 RetryException 异常的,如果这里不catch住,会影响代码正常执行的,所以在使用中,一定要记得catch住。

@Retryable

@Retryable 这个注解这是spring自己的了,而这个注解是通过aop来实现的,所以还要引入切面的工具包

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

引入好jar包后,在启动类上添加 @EnableRetry 注解,开启重试机制。

接下来我们才可以使用@Retryable 注解,而这个注解是注释在方法上的,它还有一些参数需要配置

@Retryable注解中的参数说明:

属性值 解析
maxAttempts 最大重试次数,默认为3,如果要设置的重试次数为3,可以不写
value 抛出指定异常才会重试
include 包含处理异常,和value一样,默认为空,当exclude也为空时,所有异常都重试
exclude 指定不处理的异常,默认空,当include也为空时,所有异常都重试
backoff 重试等待策略,默认使用@Backoff value默认为1000L

@Backoff参数说明

属性值 解析
value 隔多少毫秒后重试,默认为1000L
delay 和value一样,是默认为0
multiplier 默认为0,表示固定暂停1秒后进行重试,如果把multiplier设置为1.5,则第一次重试为2秒,第二次为3秒,第三次为4.5秒。(指定延迟倍数)

代码举例

@Retryable(value={IOException.class},,maxAttempts = 5, backoff = @Backoff(value = 3000, multiplier = 1.5))

而spring也贴心的为我们考虑到了全部重试都失败后的处理方式,那就是用 @Recover 去修饰方法,让此方法成为被@Retryable修饰的方法的重试失败调用方法,而且@Recover方法的出参一定要@Retryable方法的出参保持一致。

相比较Google的Retryer,spring的注解还是更周全一些,毕竟使用Retryer我们只能在catch中处理全部调用失败的逻辑。但是在使用@Retryable时,也会经常碰到不生效的问题,还是要多注意的。

要注意的是

  1. @Retryable因为是使用到了切面,所以它和 @Transactional,@Async注解都有相似的使用方式,那就是需要新建一个service去调用,如果在同一个类中去调用的话,是不会生效的,只会调用个寂寞。说白了最好放入容器中,通过容器调用。
  2. 使用@Retryable时,不可以把异常catch,否则会检测不到
  3. 使用@Recover,方法的返回值必须与@Retryable方法一致。方法的第一个参数,必须是Throwable类型的,建议是与@Retryable配置的异常一致,其他的参数,需要与@Retryable方法的参数一致。否则很容易不会生效。
@Recover
public BaseResp recover(Throwable e, boolean isRetry) throws Exception{log.err("重试失败,执行recover");return BaseResp.success("recover方法以执行");
}

参考资料:

https://blog.csdn.net/wtopps/article/details/103698635
https://www.cnblogs.com/juncaoit/p/11398547.html

Java重发机制的实现相关推荐

  1. Java 轮询(重发)机制

    1.需求: (1):支付成功需要给商户发送异步通知接口 (2):发送失败得情况下需要有重发机制,重发10次,如果10次还是失败,就不重发了 2.思路 : (1):创建表,需要有时间,次数,状态,推送信 ...

  2. ActiveMQ的消息重发机制

    本文以ActiveMQ最新的5.10版本为准. 大家知道,JMS规范中,Message消息头接口中有setJMSRedelivered(boolean redelivered)和getJMSRedel ...

  3. java事件处理模型_从零开始理解JAVA事件处理机制(3)

    我们连续写了两小节的教师-学生的例子,必然觉得无聊死了,这样的例子我们就是玩上100遍,还是不知道该怎么写真实的代码.那从本节开始,我们开始往真实代码上面去靠拢. 事件最容易理解的例子是鼠标事件:我们 ...

  4. 两道面试题,带你解析Java类加载机制

    2019独角兽企业重金招聘Python工程师标准>>> 在许多Java面试中,我们经常会看到关于Java类加载机制的考察,例如下面这道题: class Grandpa {static ...

  5. Java类加载机制详解【java面试题】

    Java类加载机制详解[java面试题] (1)问题分析: Class文件由类装载器装载后,在JVM中将形成一份描述Class结构的元信息对象,通过该元信息对象可以获知Class的结构信息:如构造函数 ...

  6. 利用java反射机制 读取配置文件 实现动态类载入以及动态类型转换

    作者:54dabang 在spring的学习过程之中,我们能够看出通过配置文件来动态管理bean对象的优点(松耦合 能够让零散部分组成一个总体,而这些总体并不在意之间彼此的细节,从而达到了真正的物理上 ...

  7. Struts2中action接收参数的三种方法及ModelDriven跟Preparable接口结合JAVA反射机制的灵活用法...

    Struts2中action接收参数的三种方法及ModelDriven跟Preparable接口结合JAVA反射机制的灵活用法 www.MyException.Cn   发布于:2012-09-15 ...

  8. 谈谈 Java 类加载机制

    点击上方"方志朋",选择"置顶或者星标" 你的关注意义重大! 来源:Rainstorm , github.com/c-rainstorm/blog/blob/m ...

  9. Java反射机制分析指南

    一.JAVA是动态语言吗? 一般而言,说到动态言,都是指在程序运行时允许改变程序结构或者变量类型,从这个观点看,JAVA和C++一样,都不是动态语言. 但JAVA它却有着一个非常突出的动态相关机制:反 ...

最新文章

  1. [实现]Javascript代码的另一种压缩与加密方法——代码图片转换
  2. 安卓常用功能——已封装好
  3. JSP的学习二(请求转发与 重定向)
  4. 2小时撸完代码之后,所有程序员都逃不过的一天... (强共鸣)
  5. github开源项目_GitHub项目分析,3D打印义肢和更多开源新闻
  6. Guice系列之用户指南(八)
  7. 散热器老化引起电脑死机
  8. 自动驾驶汽车也能聊天?
  9. linkin大话设计模式--抽象工厂
  10. QT设置画笔/画刷颜色
  11. tomcat8下载时各个版本的说明
  12. panasonic打印机驱动下载
  13. LDC1314和LDC1312的使用
  14. [html] 微软雅黑是有版权的,在页面中使用font-family:Microsoft YaHei会不会有版权问题呢?
  15. cad lisp 背景遮罩_单行文字转多行文字带背景遮罩
  16. magento-onestep-checkout-remove-payment-method-step
  17. halcon一维码识别
  18. 线性代数教程 线性方程组
  19. LonelyWriter for Mac(小黑屋写作软件)
  20. 计算机应用软件开机自动启动设置,电脑开机软件自动启动怎么关闭 win7/win10快速关闭开机自启软件...

热门文章

  1. 强强联合!东南大学-南京信息工程大学共建医学人工智能联合研究院
  2. stax2 jar 包冲突
  3. DSM调制器原理及simulink仿真分析
  4. Java实现微信小程序授权手机号登陆(史上最简单)
  5. 这是一本数学书还是一本编程书?学好数学,让你成为更好的程序员
  6. Memcached分布式算法详解
  7. 像ChatGPT玩转Excel数据
  8. 订阅号助手服务器繁忙,微信订阅号助手1.0上线3天后感想,方便查看数据、只能简单编辑...
  9. 微信小程序中从缓存storage获取的的数据
  10. Java获取任意字符串的首字母