原文地址:https://www.jianshu.com/p/ee02d6125113

需求背景:

有些时候我们再调用一些第三方服务的时候,从第三方那边拉数据。

但是第三方服务不是100%稳定的,有些时候会抽风一下,导致我们的调用失败,整个调用链就失败。整个时候需要触发重试,而且不是一直死循环重试,因为第三方服务器不稳定的情况下一直循环也是大概率失败,而是应该每隔一段时间重试一次,例如第二次重试是30s后,第三次重试是60s后,第四次重试是120s后如此类推。

这个时候我就想到到spring-retry,下面是spring-retry的使用教学

一、新建springboot工程

二、引入依赖

org.springframework.boot
spring-boot-starter-web

org.springframework.retry
spring-retry

org.aspectj
aspectjweaver
1.9.2

org.springframework.boot
spring-boot-starter-test
test

org.junit.vintage
junit-vintage-engine

三、编写测试类

@Component

@EnableRetry

public class RetryService {

@Retryable(maxAttempts = 5,backoff = @Backoff(multiplier = 2,value = 2000L,maxDelay = 10000L))
public void retry(){
System.out.println(new Date());
throw new RuntimeException("retry异常");
}

}

其中要在测试类上面打注解@EnableRetry,测试方法上面打注册@Retryable,'@Retryable'注解中,maxAttempts是最大尝试次数,backoff是重试策略,value 是初始重试间隔毫秒数,默认是3000l,multiplier是重试乘数,例如第一次是3000l,第二次是3000lmultiplier,第三次是3000lmultiplier2如此类推,maxDelay是最大延迟毫秒数,如果3000lmultiplier*n>maxDelay,延时毫秒数会用maxDelay。

四、运行单元测试类,效果如下

image.png

延迟时间分别是2、4、8、10s。

实现原理

我们可以通过写一个自己的注解去实现同样的逻辑

@MyBackoff注解类

@Target({ElementType.TYPE})

@Retention(RetentionPolicy.RUNTIME)

public @interface MyBackoff {

long value() default 1000L;

long maxDelay() default 0L;

double multiplier() default 0.0D;

}

@MyRetryable注解类

@Target({ElementType.TYPE})

@Retention(RetentionPolicy.RUNTIME)

public @interface MyBackoff {

long value() default 1000L;

long maxDelay() default 0L;

double multiplier() default 0.0D;

}

aop切面类

@Component

@Aspect

public class Aop {

protected org.slf4j.Logger logger = org.slf4j.LoggerFactory.getLogger(this.getClass());

@Pointcut("@annotation(com.eujian.springretry.myanno.MyRetryable)")
public void pointCutR() {
}
/**
* 埋点拦截器具体实现
*/
@Around("pointCutR()")
public Object methodRHandler(ProceedingJoinPoint joinPoint) throws Throwable {
MethodSignature methodSignature = (MethodSignature)joinPoint.getSignature();
Method targetMethod = methodSignature.getMethod();
MyRetryable myRetryable = targetMethod.getAnnotation(MyRetryable.class);
MyBackoff backoff = myRetryable.backoff();
int maxAttempts = myRetryable.maxAttempts();
long sleepSecond = backoff.value();
double multiplier = backoff.multiplier();
if(multiplier<=0){
multiplier = 1;
}
Exception ex = null;
int retryCount = 1;
do{
try { Object proceed = joinPoint.proceed();
return proceed;
}catch (Exception e){
logger.info("睡眠{}毫秒",sleepSecond);
Thread.sleep(sleepSecond);
retryCount++;
sleepSecond = (long)(multiplier)*sleepSecond;
if(sleepSecond>backoff.maxDelay()){
sleepSecond = backoff.maxDelay();
logger.info("睡眠时间太长,改成{}毫秒",sleepSecond);
}
ex = e; }
}while (retryCount<maxAttempts); throw ex;
}

}

运行测试类效果

用spring-retry注解自动触发重试方法的更多相关文章

  1. Spring基于注解自动装配

    前面我们介绍Spring IoC装载的时候,使用XML配置这种方法来装配Bean,这种方法可以很直观的看到每个Bean的依赖,但缺点也很明显:写起来非常繁琐,每增加一个组件,就必须把新的Bean配置到 ...

  2. Spring @Autowired 注解自动注入流程是怎么样?

    面试中碰到面试官问:"Spring 注解是如果工作的?",当前我一惊,完了这不触及到我的知识误区了吗?,还好我机智,灵机一动回了句:Spring 注解的工作流程倒还没有看到,但是我 ...

  3. spring retry注解

    <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/20 ...

  4. with一个对象,自动触发__enter__方法

    class Foo(object): def __init__(self): pass def __enter__(self): print("__enter__") def __ ...

  5. Spring Retry 重试

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

  6. Spring retry实践

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

  7. Spring异常重试框架Spring Retry

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

  8. 异常重试框架Spring Retry实践

    前期准备在Maven项目中添加Spring Retry和切面的依赖 POM: <!-- Spring Retry --> <dependency> <groupId> ...

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

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

随机推荐

  1. 使用 jsDelivr 免费加速 GitHub Pages 博客的静态资源(二)

    之前写过一篇 使用 jsDelivr 免费加速 GitHub Pages 博客的静态资源,在那之后,又陆续想到并实施了几点利用 jsDelivr 进一步加速静态资源加载的措施,新起一篇作为记录和分享. ...

  2. ansible-playbook定义变量与使用

    1. ansible-playbook变量定义与使用 命令行 在Inventory中定义 在Playbook中定义 在Role中定义 注册变量(register) 系统信息变量(facts) 2. 在 ...

  3. 多测师讲解python _类(原始版)_高级讲师肖sir

    # Python中的类: '''定义一个类:class +名称=类 在类当中定义:def +名称=实例方法(self)与类平齐def +名称=普通函数定义一个函数:def +名称=函数在函数中:函数( ...

  4. IDEA项目区模块文件变为红色解决办法

    解决方法 先检查文件格式是否为.java格式..class格式就不行. 选择file–>setting–>version Controller,然后把vcs选项选择为none

  5. 物联网wifi模块

    物联网wifi模块 物联网wifi模块 是上海卓岚推出的MQTT+JSON转Modbus物联网WiFi核心模块.支持以MQTT的方式连接云端服务器,支持可以界面话配置,自主采集Modbus仪表/645 ...

  6. 【C语言C++编程入门】——编译机制和语言标准!

    编程机制 编写程序时必须遵循确切步骤主要是取决于你的计算机环境.因为 C语言是可以移植的,所以它在许多环境中可用,其中包括 UNIX,Linux,Windows等等 . 不过,让我们首先来看一看许多环 ...

  7. Navicat Premium_11.2.7简体中文版 破解版本 windows版本 失效

    亲测可用 自己一直在用的 https://pan.baidu.com/s/1VVKKQoIKVB0BgNXBK4YTrQ

  8. ASP.NET 获取客户端IP地址

    我们用Request.ServerVariables( "REMOTE_ADDR ")   来取得客户端的IP地址, 但如果客户端是使用代理服务器来访问,那取到的就是代理服务器的I ...

  9. vue知识点12

    知识点归纳整理如下: 1. 数组用下标改变,或者对象增加属性,这样的改变数据   是不能触发视图更新的,要用 Vue.set(对象,属性,值) 或this.$set(对象,属性,值) 2. this. ...

  10. Spring+Hibernate+Struts2整合之实现登录功能

    前端代码: <form id="loginForm" action="${ pageContext.request.contextPath }/user_login ...