由于场景需求,需要对项目依赖的部分外部接口添加响应超时重试机制,而Spring Retry可以满足该需求,因此进行相关资料查询。对于Spring Retry的使用,网上的资料主要是采用注解的方式实现,而对于依赖的外部接口的重试,为避免对将重试机制添加到业务逻辑中,优先采用xml配置的方式实现。本文主要对https://www.baeldung.com/spring-retry中的教程进行翻译。


Spring Retry提供了自动调用失败操作的功能,这有助于解决瞬时错误(如网络抖动)。Spring Retry提供对流程和基于策略行为的声明式控制,易于扩展和自定义。

本文主要介绍Spring Retry在Spring项目中实现重试机制,同时也会配置监听者获取额外的回调结果。

Maven坐标

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

可以在https://search.maven.org/classic/#search%7Cga%7C1%7Cg%3A%22org.springframework.retry%22%20AND%20a%3A%22spring-retry%22查看Retry的最新版本

使能Spring Retry

可以通过两种方式使能Spring Retry。

1、@EnableRetry注解方式

@Configuration
@EnableRetry
public class AppConfig { ... }

2、xml文件配置

<bean class="org.springframework.retry.annotation.RetryConfiguration" />

Retry实现方式

Retry的用法有三种,Annotation形式、RetryTemplate形式、XML形式。

注解方式

1、@Retryable

将@Retryable添加在需要重试的方法,即retryService()。

@Service
public class MyService {@Retryable(value = { SQLException.class }, maxAttempts = 2,backoff = @Backoff(delay = 5000))void retryService(String sql) throws SQLException{...}...
}
/*
value:抛出指定异常才会重试
include:和value一样,默认为空,当exclude也为空时,默认所以异常
exclude:指定不处理的异常
maxAttempts:最大重试次数,默认3次
backoff:重试等待策略,默认使用@Backoff,@Backoff的value默认为1000L,我们设置为2000L;multiplier(指定延迟倍数)默认为0,表示固定暂停1秒后进行重试,如果把multiplier设置为1.5,则第一次重试为2秒,第二次为3秒,第三次为4.5秒。
*/

上面的示例中,在方法出现SQLException异常时,将会重试2次,每次间隔5000ms。如果@Retryable没有任何说明,默认在方法抛出异常时,将会重试3次,每次间隔1s。

2、@Recover

在@Retryable方法重试后依然失败,调用@Recover定义的一个单独恢复方法。

@Service
public class MyService {...@Recovervoid recover(SQLException e, String sql){...}
}

如果retryService()方法抛出SQLException,将会调用recover()方法。合适的恢复处理程序的第一个参数类型为Throwable(可选)。 后续参数从失败方法的参数列表中填充,其顺序与失败方法的顺序相同,并且具有相同的返回类型。

RetryTemplate方式

1、RetryOperations

Spring Retry提供RetryOperations接口,提供一系列的execute()方法:

public interface RetryOperations {<T> T execute(RetryCallback<T> retryCallback) throws Exception;...
}

其中,参数RetryCallback是在业务逻辑失败后执行的接口:

public interface RetryCallback<T> {T doWithRetry(RetryContext context) throws Throwable;
}

2、RetryTemplate配置

RetryTemplate是RetryOperations的实现类。在@Configuration类中可以配置RetryTemplate:

@Configuration
public class AppConfig {//...@Beanpublic RetryTemplate retryTemplate() {RetryTemplate retryTemplate = new RetryTemplate();FixedBackOffPolicy fixedBackOffPolicy = new FixedBackOffPolicy();fixedBackOffPolicy.setBackOffPeriod(2000l);retryTemplate.setBackOffPolicy(fixedBackOffPolicy);SimpleRetryPolicy retryPolicy = new SimpleRetryPolicy();retryPolicy.setMaxAttempts(2);retryTemplate.setRetryPolicy(retryPolicy);return retryTemplate;}
}

RetryPolicy接口确定方法重试的规则。SimpleRetryPolicy类用于设置失败方法的固定重试次数。

BackOffPolicy接口用于控制不同的重试的间隔。FixedBackOffPolicy类设置失败方法以固定的时间间隔进行重试。

3、应用RetryTemplate

通过调用retryTemplate.execute()使方法具有重试功能:

retryTemplate.execute(new RetryCallback<Void, RuntimeException>() {@Overridepublic Void doWithRetry(RetryContext arg0) {myService.templateRetryService();...}
});

其中,templateRetryService()为需要调用失败后重试的方法。

可以采用lambda表达式代替匿名类:

retryTemplate.execute(arg0 -> {myService.templateRetryService();return null;
});

XML方式

Spring Retry可以通过xml使用Spring AOP命名空间配置。

1、添加xml文件

在classpath中添加retryadvic.xml。

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xmlns:aop="http://www.springframework.org/schema/aop"xsi:schemaLocation="http://www.springframework.org/schema/aophttp://www.springframework.org/schema/aop/spring-aop.xsdhttp://www.springframework.org/schema/beanshttp://www.springframework.org/schema/beans/spring-beans.xsd"><aop:config><aop:pointcut id="transactional"expression="execution(*MyService.xmlRetryService(..))" /><aop:advisor pointcut-ref="transactional"advice-ref="taskRetryAdvice" order="-1" /></aop:config><bean id="taskRetryAdvice"class="org.springframework.retry.interceptor.RetryOperationsInterceptor"><property name="RetryOperations" ref="taskRetryTemplate" /></bean><bean id="taskRetryTemplate"class="org.springframework.retry.support.RetryTemplate"><property name="retryPolicy" ref="taskRetryPolicy" /><property name="backOffPolicy" ref="exponentialBackOffPolicy" /></bean><bean id="taskRetryPolicy"class="org.springframework.retry.policy.SimpleRetryPolicy"><constructor-arg index="0" value="5" /><constructor-arg index="1"><map><entry key="java.lang.RuntimeException" value="true" /></map></constructor-arg></bean><bean id="exponentialBackOffPolicy"class="org.springframework.retry.backoff.ExponentialBackOffPolicy"><property name="initialInterval" value="300"></property><property name="maxInterval" value="30000"></property><property name="multiplier" value="2.0"></property></bean>
</beans>

这是通过xml配置了RetryTemplate。其中,MyService.xmlRetryService()为需要失败后重试的方法。retryPolicy定义重试次数和捕获的异常。backOffPolicy定义不同重试间的时间间隔等。(如上文的FixedBackOffPolicy类,ExponentialBackOffPolicy类未深入研究)

2、xml配置的应用

导入retryadvice.xml到classpath,并使能@AspectJ:

@Configuration
@EnableRetry
@EnableAspectJAutoProxy
@ImportResource("classpath:/retryadvice.xml")
public class AppConfig { ... }

同样,可以采用xml配置的方式完成:

<!-- 导入retryadvice.xml -->
<import resource="retryadvice.xml"/><!-- 使能aop -->
<aop:aspectj-autoproxy /><!-- 使能Spring Retry,相当于@EnableRetry -->
<bean class="org.springframework.retry.annotation.RetryConfiguration" />

监听者(Listeners)

注:笔者未验证这一章节

Listeners在重试时提供额外的回调。它们可以用于不同重试之间的各种横切关注点。

1、添加回调(Callbacks)

回调函数在RetryListener接口中提供:

public class DefaultListenerSupport extends RetryListenerSupport {@Overridepublic <T, E extends Throwable> void close(RetryContext context,RetryCallback<T, E> callback, Throwable throwable) {logger.info("onClose");...super.close(context, callback, throwable);}@Overridepublic <T, E extends Throwable> void onError(RetryContext context,RetryCallback<T, E> callback, Throwable throwable) {logger.info("onError"); ...super.onError(context, callback, throwable);}@Overridepublic <T, E extends Throwable> boolean open(RetryContext context,RetryCallback<T, E> callback) {logger.info("onOpen");...return super.open(context, callback);}
}

打开和关闭回调出现在整个重试之前和之后,onError应用于各个RetryCallback调用。

2、注册Listener

接着,将Listiener(DefaultListenerSupport)注册到RetryTemplate bean:

@Configuration
public class AppConfig {...@Beanpublic RetryTemplate retryTemplate() {RetryTemplate retryTemplate = new RetryTemplate();...retryTemplate.registerListener(new DefaultListenerSupport());return retryTemplate;}
}

3、测试

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = AppConfig.class,loader = AnnotationConfigContextLoader.class)
public class SpringRetryTest {@Autowiredprivate MyService myService;@Autowiredprivate RetryTemplate retryTemplate;@Test(expected = RuntimeException.class)public void givenTemplateRetryService_whenCallWithException_thenRetry() {retryTemplate.execute(arg0 -> {myService.templateRetryService();return null;});}
}

当运行测试用例时,下面的日志文本表示我们已经成功配置了RetryTemplate和Listener:

2017-01-09 20:04:10 [main] INFO  o.b.s.DefaultListenerSupport - onOpen
2017-01-09 20:04:10 [main] INFO  o.baeldung.springretry.MyServiceImpl
- throw RuntimeException in method templateRetryService()
2017-01-09 20:04:10 [main] INFO  o.b.s.DefaultListenerSupport - onError
2017-01-09 20:04:12 [main] INFO  o.baeldung.springretry.MyServiceImpl
- throw RuntimeException in method templateRetryService()
2017-01-09 20:04:12 [main] INFO  o.b.s.DefaultListenerSupport - onError
2017-01-09 20:04:12 [main] INFO  o.b.s.DefaultListenerSupport - onClose

注意事项(转载参考资料4)

1、使用了@Retryable的方法不能在本类被调用,不然重试机制不会生效。也就是要标记为@Service,然后在其它类使用@Autowired注入或者@Bean去实例才能生效。

2 、要触发@Recover方法,那么在@Retryable方法上不能有返回值,只能是void才能生效。

3 、非幂等情况下慎用

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

参考资料

1、https://www.baeldung.com/spring-retry(原文链接)

https://github.com/eugenp/tutorials/tree/master/spring-all(源代码链接)

2、https://www.jianshu.com/p/cc7abf831900

3、https://www.jianshu.com/p/58e753ca0151

4、https://my.oschina.net/wangjunBlog/blog/1889015

Spring Retry使用相关推荐

  1. foxmail邮件加载失败重试_java retry(重试) spring retry, guava retrying 详解

    系列说明 java retry 的一步步实现机制. java-retry 源码地址 情景导入 简单的需求 产品经理:实现一个按条件,查询用户信息的服务. 小明:好的.没问题. 代码 UserServi ...

  2. java retry(重试) spring retry, guava retrying 详解

    转载 自 http://blog.51cto.com/9250070/2156431 系列说明 java retry 的一步步实现机制. java-retry 源码地址 情景导入 简单的需求 产品经理 ...

  3. 针对Spring的Spring Retry 我发现了这样一个大家都不知道的技巧!

    外部服务对于调用者来说一般都是不可靠的,尤其是在网络环境比较差的情况下,网络抖动很容易导致请求超时等异常情况,这时候就需要使用失败重试策略重新调用 API 接口来获取.重试策略在服务治理方面也有很广泛 ...

  4. Spring Retry 重试机制实现及原理

    概要 Spring实现了一套重试机制,功能简单实用.Spring Retry是从Spring Batch独立出来的一个功能,已经广泛应用于Spring Batch,Spring Integration ...

  5. java retry 设置上限_java-如何设置Spring Retry模板重试最大尝试次数:无限

    我想用Spring Retry修改数据库连接的创建,以便在应用程序启动时数据库关闭时再试一次.我不想限制重试次数.我应该如何配置策略来做到这一点. 我当前的代码(我知道在这种状态下它限制为100): ...

  6. 高级JAVA - 手写简单的重试组件学习Spring Retry

    目录 一 . 定义注解 二 . 利用cglib代理扩展重试业务 三 . 编写代理类 , 使用自定义的XRetryInterceptor作为拦截器 四 . 编写相关业务方法 , 测试代码 五 . 测试结 ...

  7. Spring异常重试框架Spring Retry

    在调用第三方接口或者使用mq时,会出现网络抖动,连接超时等网络异常,所以需要重试.为了使处理更加健壮并且不太容易出现故障,后续的尝试操作,有时候会帮助失败的操作最后执行成功.例如,由于网络故障或数据库 ...

  8. Spring retry重试框架

    spring retry是从spring batch独立出来的一个能功能,主要实现了重试和熔断.对于重试是有场景限制的,不是什么场景都适合重试,比如参数校验不合法.写操作等(要考虑写是否幂等)都不适合 ...

  9. Spring Retry # Stateful Retry

    Spring Retry中的重试,分为无状态的重试和有状态的重试: 简述 有状态重试通常是用在message-driven 的应用中,从消息中间件比如RabbitMQ等接收到的消息,如果应用处理失败, ...

  10. 重试框架Spring retry实践

    spring retry是从spring batch独立出来的一个能功能,主要实现了重试和熔断.对于重试是有场景限制的,不是什么场景都适合重试,比如参数校验不合法.写操作等(要考虑写是否幂等)都不适合 ...

最新文章

  1. javascript 之 push方法
  2. Java 获得方法调用者名称
  3. mysql group by与order by的研究--分类中最新的内容
  4. 大剑无锋之Hadoop的三个作业调度器【面试推荐】
  5. 耗时两年,19岁小伙采用230片纯74逻辑芯片搭建出32位Risc-V处理器!可玩贪吃蛇...
  6. c语言case多语句的取值,Switch Case语句中多个值匹配同一个代码块的写法
  7. 页面url带参数_微信小程序云开发教程微信小程序的JS高级页面间数据传递
  8. 数据结构python语言描述课后答案_《数据结构与算法Python语言描述》习题第二章第三题(python版)...
  9. Visual Studio 2017 15.9 Previews扩展C++调试功能
  10. 系统同传软件_语情快递 | 手语同传AI,你见过吗?
  11. GMSK调制 MATLAB代码
  12. vmware虚拟机ping不通主机
  13. 使用VMwaver 克隆CentOS 6.9网卡配置报错
  14. ews java api maven_通过 EWS JAVA API读取exchange邮件
  15. mplayer全参数
  16. 02优先队列和索引优先队列-优先队列-数据结构和算法(Java)
  17. ElementUI分页与增删改合并
  18. rk3288实践:第一个嵌入式Linux驱动程序
  19. android 获取当前时间精确到毫秒的两种方法
  20. outlook的archive pst丢失后

热门文章

  1. 【mysql修改host远程访问】
  2. Jmeter 监控Windows服务器资源(端口4444)
  3. IntelliJ IDEA 2020.2后版本复制文本出现NBSP解决方案
  4. unity VR游戏帧数优化(二)
  5. 鉴智机器人完成亿元人民币级别的A轮融资第二次交割,由深创投和厚雪基金联合领投
  6. HTML CSS 浮动练习(仿猫眼电影)
  7. C++两个函数可以相互递归吗_C语言(7)- 递归
  8. VS Code去掉 unused 警告
  9. 论脑电波、磁场、鬼三者之关系
  10. 大多数人还没有准备好回到办公室,但他们对恢复正常生活更有信心