前期准备
在Maven项目中添加Spring Retry和切面的依赖

POM:

        <!-- Spring Retry -->

        <dependency>

            <groupId>org.springframework.retry</groupId>

            <artifactId>spring-retry</artifactId>

        </dependency>

        <dependency>

            <groupId>org.aspectj</groupId>

            <artifactId>aspectjweaver</artifactId>

        </dependency>

注解方式实践
Service类:@Retryable创建了一个最大重试次数为3,最初重试等待时间为5秒,重试等待时间倍率为1的接口;@Recover当@Retryable接口重试3次后依然不成功则会运行。

package com.jsoft.springboottest.springboottest1;
import java.time.LocalTime;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.remoting.RemoteAccessException;
import org.springframework.retry.annotation.Backoff;
import org.springframework.retry.annotation.Recover;
import org.springframework.retry.annotation.Retryable;
import org.springframework.stereotype.Service; @Service
public class RemoteService { private final static Logger logger = LoggerFactory.getLogger(RemoteService.class); @Retryable(value = { RemoteAccessException.class }, maxAttempts = 3, backoff = @Backoff(delay = 5000l, multiplier = 1))
public void call() throws Exception {
logger.info(LocalTime.now()+" do something...");
throw new RemoteAccessException("RPC调用异常");
} @Recover
public void recover(RemoteAccessException e) {
logger.info(e.getMessage());
}
}

Controller:创建一个调用重试接口的接口

package com.jsoft.springboottest.springboottest1.controller;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController; import com.jsoft.springboottest.springboottest1.RemoteService; @RestController
public class TestController { @Autowired
private RemoteService remoteService; @RequestMapping("/show")
public String show(){
try {
remoteService.call();
} catch (Exception e) {
e.printStackTrace();
}
return "Hello World";
}
}

运行结果如下:

2018-11-07 17:01:55.463  INFO 8136 --- [nio-8080-exec-1] c.e.demo.service.impl.RetryServiceImpl   : 17:01:55.463 do something...
2018-11-07 17:02:00.463  INFO 8136 --- [nio-8080-exec-1] c.e.demo.service.impl.RetryServiceImpl   : 17:02:00.463 do something...
2018-11-07 17:02:05.463  INFO 8136 --- [nio-8080-exec-1] c.e.demo.service.impl.RetryServiceImpl   : 17:02:05.463 do something...
2018-11-07 17:02:05.464  INFO 8136 --- [nio-8080-exec-1] c.e.demo.service.impl.RetryServiceImpl   : RPC调用异常
注意:

1、使用了@Retryable的方法里面不能使用try...catch包裹,要在方法上抛出异常,不然不会触发。

2、在重试期间这个方法是同步的,如果使用类似Spring Cloud这种框架的熔断机制时,可以结合重试机制来重试后返回结果。

3、@Recover 用于@Retryable重试失败后处理方法,此注解注释的方法参数一定要是@Retryable抛出的异常,否则无法识别,可以在该方法中进行日志处理。

注解简介
3.1  @Retryable注解

被注解的方法发生异常时会重试

value:指定发生的异常进行重试 
include:和value一样,默认空,当exclude也为空时,所有异常都重试 
exclude:指定异常不重试,默认空,当include也为空时,所有异常都重试 
maxAttemps:重试次数,默认3 
backoff:重试补偿机制,默认没有
3.2  @Backoff注解

delay:指定延迟后重试 
multiplier:指定延迟的倍数,默认为1。比如delay=5000l,multiplier=2时,第一次重试为5秒后,第二次为5*2=10秒,第三次为5*2*2=20秒
3.3  @Recover

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

3.4 @EnableRetry

开启重试。

非注解方式实践
Spring Retry不只能注入方式去实现,还可以通过API的方式实现,类似熔断处理的机制就基于API方式实现会比较宽松。

代码如上面所示,只需要修改Service中的API。

public void test() {

    final RetryTemplate retryTemplate = new RetryTemplate();

    final SimpleRetryPolicy policy = new SimpleRetryPolicy(3, Collections.<Class<? extends Throwable>, Boolean>  singletonMap(Exception.class, true));

    ExponentialBackOffPolicy ePolicy = new ExponentialBackOffPolicy();

    ePolicy.setMultiplier(2);//等待时间倍率

    ePolicy.setInitialInterval(2000l);//重试间隔时间,2秒

    ePolicy.setMaxInterval(30 * 60 * 1000l);//最大重试间隔时间为30分钟

    retryTemplate.setRetryPolicy(policy);

    retryTemplate.setBackOffPolicy(ePolicy);

    final RetryCallback<Object, Exception> retryCallback = new RetryCallback<Object, Exception>() {

        public Object doWithRetry(RetryContext context) throws Exception {

            logger.info(LocalTime.now() + "do some thing,第" + context.getRetryCount() + "次重试");
//设置context一些属性,给RecoveryCallback传递一些属性
context.setAttribute("key1", "value1");
throw new Exception("RPC调用异常"); } }; // 如果RetryCallback执行出现指定异常, 并且超过最大重试次数依旧出现指定异常的话,就执行RecoveryCallback动作 final RecoveryCallback<Object> recoveryCallback = new RecoveryCallback<Object>() { public Object recover(RetryContext context) throws Exception { logger.info(context.getLastThrowable().getMessage());
return null; } }; try { final Object execute = retryTemplate.execute(retryCallback, recoveryCallback); } catch (Exception e) { e.printStackTrace(); } }
public void test() { final RetryTemplate retryTemplate = new RetryTemplate(); final SimpleRetryPolicy policy = new SimpleRetryPolicy(3, Collections.<Class<? extends Throwable>, Boolean> singletonMap(Exception.class, true)); ExponentialBackOffPolicy ePolicy = new ExponentialBackOffPolicy(); ePolicy.setMultiplier(2);//等待时间倍率 ePolicy.setInitialInterval(2000l);//重试间隔时间,2秒 ePolicy.setMaxInterval(30 * 60 * 1000l);//最大重试间隔时间为30分钟 retryTemplate.setRetryPolicy(policy); retryTemplate.setBackOffPolicy(ePolicy); final RetryCallback<Object, Exception> retryCallback = new RetryCallback<Object, Exception>() { public Object doWithRetry(RetryContext context) throws Exception { logger.info(LocalTime.now() + "do some thing,第" + context.getRetryCount() + "次重试"); //设置context一些属性,给RecoveryCallback传递一些属性 context.setAttribute("key1", "value1"); throw new Exception("RPC调用异常"); } }; // 如果RetryCallback执行出现指定异常, 并且超过最大重试次数依旧出现指定异常的话,就执行RecoveryCallback动作 final RecoveryCallback<Object> recoveryCallback = new RecoveryCallback<Object>() { public Object recover(RetryContext context) throws Exception { logger.info(context.getLastThrowable().getMessage()); return null; } }; try { final Object execute = retryTemplate.execute(retryCallback, recoveryCallback); } catch (Exception e) { e.printStackTrace(); } }

说明:

1. 设置delay、maxDealy、multiplier,使用 ExponentialBackOffPolicy(指数级重试间隔的实现 ),multiplier即指定延迟倍数,比如delay=5000l,multiplier=2,则第一次重试为5秒,第二次为10秒,第三次为20秒……

2.通过上述方式可以获取到重试的上下文RetryContext,包含三个参数count,lastException,exhausted,可以看到上面代码中的红色部分。比如:[RetryContext: count=3, lastException=java.lang.RuntimeException: 当前步骤没有定义执行代理, exhausted=false]

运行结果如下:

2018-11-07 18:06:19.118  INFO 7776 --- [nio-8080-exec-1] c.e.demo.service.impl.RetryServiceImpl   : 18:06:19.118do some thing,第0次重试
2018-11-07 18:06:21.119  INFO 7776 --- [nio-8080-exec-1] c.e.demo.service.impl.RetryServiceImpl   : 18:06:21.119do some thing,第1次重试
2018-11-07 18:06:25.120  INFO 7776 --- [nio-8080-exec-1] c.e.demo.service.impl.RetryServiceImpl   : 18:06:25.120do some thing,第2次重试
2018-11-07 18:06:25.120  INFO 7776 --- [nio-8080-exec-1] c.e.demo.service.impl.RetryServiceImpl   : RPC调用异常
类方法简介
5.1 spring-retry 结构

5.2 概览

RetryCallback: 封装你需要重试的业务逻辑(上文中的doSth)

RecoverCallback:封装在多次重试都失败后你需要执行的业务逻辑(上文中的doSthWhenStillFail)

RetryContext: 重试语境下的上下文,可用于在多次Retry或者Retry 和Recover之间传递参数或状态(在多次doSth或者doSth与doSthWhenStillFail之间传递参数)

RetryOperations : 定义了“重试”的基本框架(模板),要求传入RetryCallback,可选传入RecoveryCallback;

RetryListener:典型的“监听者”,在重试的不同阶段通知“监听者”(例如doSth,wait等阶段时通知)

RetryPolicy : 重试的策略或条件,可以简单的进行多次重试,可以是指定超时时间进行重试(上文中的someCondition)

BackOffPolicy: 重试的回退策略,在业务逻辑执行发生异常时。如果需要重试,我们可能需要等一段时间(可能服务器过于繁忙,如果一直不间隔重试可能拖垮服务器),
当然这段时间可以是 0,也可以是固定的,可以是随机的(参见tcp的拥塞控制算法中的回退策略)。回退策略在上文中体现为wait();

RetryTemplate: RetryOperations的具体实现,组合了RetryListener[],BackOffPolicy,RetryPolicy。
5.3 重试策略
 
NeverRetryPolicy:只允许调用RetryCallback一次,不允许重试

AlwaysRetryPolicy:允许无限重试,直到成功,此方式逻辑不当会导致死循环

SimpleRetryPolicy:固定次数重试策略,默认重试最大次数为3次,RetryTemplate默认使用的策略

TimeoutRetryPolicy:超时时间重试策略,默认超时时间为1秒,在指定的超时时间内允许重试

ExceptionClassifierRetryPolicy:设置不同异常的重试策略,类似组合重试策略,区别在于这里只区分不同异常的重试

CircuitBreakerRetryPolicy:有熔断功能的重试策略,需设置3个参数openTimeout、resetTimeout和delegate

CompositeRetryPolicy:组合重试策略,有两种组合方式,乐观组合重试策略是指只要有一个策略允许重试即可以,
悲观组合重试策略是指只要有一个策略不允许重试即可以,但不管哪种组合方式,组合中的每一个策略都会执行
5.4 重试回退策略
重试回退策略,指的是每次重试是立即重试还是等待一段时间后重试。

默认情况下是立即重试,如果需要配置等待一段时间后重试则需要指定回退策略BackoffRetryPolicy。

NoBackOffPolicy:无退避算法策略,每次重试时立即重试

FixedBackOffPolicy:固定时间的退避策略,需设置参数sleeper和backOffPeriod,sleeper指定等待策略,默认是Thread.sleep,即线程休眠,backOffPeriod指定休眠时间,默认1秒

UniformRandomBackOffPolicy:随机时间退避策略,需设置sleeper、minBackOffPeriod和maxBackOffPeriod,该策略在[minBackOffPeriod,maxBackOffPeriod之间取一个随机休眠时间,minBackOffPeriod默认500毫秒,maxBackOffPeriod默认1500毫秒

ExponentialBackOffPolicy:指数退避策略,需设置参数sleeper、initialInterval、maxInterval和multiplier,initialInterval指定初始休眠时间,默认100毫秒,maxInterval指定最大休眠时间,默认30秒,multiplier指定乘数,即下一次休眠时间为当前休眠时间*multiplier

ExponentialRandomBackOffPolicy:随机指数退避策略,引入随机乘数可以实现随机乘数回退

深入探索
1、spring-retry通过AOP实现对目的方法的封装,执行在当前线程下,所以重试过程中当前线程会堵塞。如果BackOff时间设置比较长,最好起异步线程重试(也可以加@Async注解)。

参考文章:

http://www.mamicode.com/info-detail-2051957.html

http://www.cnblogs.com/jtlgb/p/6813164.html

spring retry 官网  https://github.com/spring-projects/spring-retry

重试与融断机制  http://www.broadview.com.cn/article/233

异常重试框架Spring Retry实践的更多相关文章

  1. Spring异常重试框架Spring Retry

    Spring Retry支持集成到Spring或者Spring Boot项目中,而它支持AOP的切面注入写法,所以在引入时必须引入aspectjweaver.jar包. 快速集成的代码样例: @Con ...

  2. Spring错误异常重试框架guava-retrying

    官网:https://github.com/rholder/guava-retrying Maven:https://mvnrepository.com/artifact/com.github.rho ...

  3. Spring retry实践

    在开发中,重试是一个经常使用的手段.比如MQ发送消息失败,会采取重试手段,比如工程中使用RPC请求外部服务,可能因为网络波动出现超时而采取重试手段......可以看见重试操作是非常常见的一种处理问题, ...

  4. C#异常重试通用类Retry

    //Retry机制 public static class Retry { /// <summary> /// 重试零个参数无返回值的方法 /// </summary> /// ...

  5. Spring Retry 在SpringBoot 中的应用

    Spring Boot中使用Spring-Retry重试框架 Spring Retry提供了自动重新调用失败的操作的功能.这在错误可能是暂时的(例如瞬时网络故障)的情况下很有用. 从2.2.0版本开始 ...

  6. Spring Retry 重试

    重试的使用场景比较多,比如调用远程服务时,由于网络或者服务端响应慢导致调用超时,此时可以多重试几次.用定时任务也可以实现重试的效果,但比较麻烦,用Spring Retry的话一个注解搞定所有.话不多说 ...

  7. 自己动手实践 spring retry 重试框架

    前序 马上过年了,预祝大家,新年快乐,少写bug 什么是spring retry? spring retry是从spring batch独立出来的一个能功能,主要实现了重试和熔断. 什么时候用? 远程 ...

  8. Spring框架中一个有用的小组件:Spring Retry

    1.概述 Spring Retry 是Spring框架中的一个组件, 它提供了自动重新调用失败操作的能力.这在错误可能是暂时发生的(如瞬时网络故障)的情况下很有帮助. 在本文中,我们将看到使用Spri ...

  9. Spring Batch实践

    Spring Batch在大型企业中的最佳实践 在大型企业中,由于业务复杂.数据量大.数据格式不同.数据交互格式繁杂,并非所有的操作都能通过交互界面进行处理.而有一些操作需要定期读取大批量的数据,然后 ...

随机推荐

  1. Java实现 LeetCode 668 乘法表中第k小的数(二分)

    668. 乘法表中第k小的数 几乎每一个人都用 乘法表.但是你能在乘法表中快速找到第k小的数字吗? 给定高度m .宽度n 的一张 m * n的乘法表,以及正整数k,你需要返回表中第k 小的数字. 例 ...

  2. Java实现 LeetCode 519 随机翻转矩阵

    519. 随机翻转矩阵 题中给出一个 n 行 n 列的二维矩阵 (n_rows,n_cols),且所有值被初始化为 0.要求编写一个 flip 函数,均匀随机的将矩阵中的 0 变为 1,并返回该值的位 ...

  3. Java实现 蓝桥杯VIP基础练习 矩形面积交

    描述 平面上有两个矩形,它们的边平行于直角坐标系的X轴或Y轴.对于每个矩形,我们给出它的一对相对顶点的坐标,请你编程算出两个矩形的交的面积. 输入 输入仅包含两行,每行描述一个矩形. 在每行中,给出矩 ...

  4. Java实现 LeetCode 344 反转字符串

    344. 反转字符串 编写一个函数,其作用是将输入的字符串反转过来.输入字符串以字符数组 char[] 的形式给出. 不要给另外的数组分配额外的空间,你必须原地修改输入数组.使用 O(1) 的额外空间 ...

  5. java实现罗马数字转十进制

    古罗马帝国开创了辉煌的人类文明,但他们的数字表示法的确有些繁琐,尤其在表示大数的时候,现在看起来简直不能忍受,所以在现代很少使用了.之所以这样,不是因为发明表示法的人的智力的问题,而是因为一个宗教的原 ...

  6. java实现南北朝时

    南北朝时,我国数学家祖冲之首先把圆周率值计算到小数点后六位,比欧洲早了 1100 年! 他采用的是称为"割圆法"的算法,实际上已经蕴含着现代微积分的思想. 如图[1.jpg]所示, ...

  7. 国外程序员整理的Java资源大全(全部是干货)

    原文 译者 唐尤华 翻译自 github akullpp 这里搜集了用来构建应用程序的工具. Apache Maven:Maven使用声明进行构建并进行依赖管理,偏向于使用约定而不是配置进行构建.Ma ...

  8. InnoDB存储引擎的事务

    事务的任务是保证一系列更新语句的原子性,锁的任务是解决并发访问可能导致的数据不一致问题.如果事务与事务之间存在并发操作,此时可以通过隔离级别实现事务的隔离性,从而实现数据的并发访问. 1 原子性(At ...

  9. python数据类型转换&&格式化输出

    ①python的数据类型包含:数字.字符串.列表.元组.字典.集合这六种基本数据类型.不同数据类型的数据可以进行类型的转换. 使用input让用户输入的数据默认为字符串类型: name = input ...

  10. java 中有几种类型的流?

    字节流,字符流. 字节流继承于 InputStream \ OutputStream, 字符流继承于 InputStreamReader \ OutputStreamWriter. 在 java.io ...