1、概述

Spring Retry 是Spring框架中的一个组件,

它提供了自动重新调用失败操作的能力。这在错误可能是暂时发生的(如瞬时网络故障)的情况下很有帮助。

在本文中,我们将看到使用Spring Retry的各种方式:注解、RetryTemplate以及回调。

2、Maven依赖

让我们首先将spring-retry依赖项添加到我们的pom.xml文件中:

  1. <dependency>
  2. <groupId>org.springframework.retry</groupId>
  3. <artifactId>spring-retry</artifactId>
  4. <version>1.2.5.RELEASE</version>
  5. </dependency>

我们还需要将Spring AOP添加到我们的项目中:

  1. <dependency>
  2. <groupId>org.springframework</groupId>
  3. <artifactId>spring-aspects</artifactId>
  4. <version>5.2.8.RELEASE</version>
  5. </dependency>

可以查看Maven Central来获取最新版本的spring-retry

spring-aspects 依赖项。

3、开启Spring Retry

要在应用程序中启用Spring Retry,我们需要将@EnableRetry注释添加到我们的@Configuration类:

  1. @Configuration
  2. @EnableRetry
  3. public class AppConfig { ... }

4、使用Spring Retry

4.1、@Retryable而不用恢复

我们可以使用@Retryable注解为方法添加重试功能:

  1. @Service
  2. public interface MyService {
  3. @Retryable(value = RuntimeException.class)
  4. void retryService(String sql);
  5. }

在这里,当抛出RuntimeException时尝试重试。

根据@Retryable的默认行为,重试最多可能发生3次,重试之间有1秒的延迟。

4.2、@Retryable@Recover

现在让我们使用@Recover注解添加一个恢复方法:

  1. @Service
  2. public interface MyService {
  3. @Retryable(value = SQLException.class)
  4. void retryServiceWithRecovery(String sql) throws SQLException;
  5. @Recover
  6. void recover(SQLException e, String sql);
  7. }

这里,当抛出SQLException时重试会尝试运行。 当@Retryable方法因指定异常而失败时,@Recover注解定义了一个单独的恢复方法。

因此,如果retryServiceWithRecovery方法在三次尝试之后还是抛出了SQLException,那么recover()方法将被调用。

恢复处理程序的第一个参数应该是Throwable类型(可选)和相同的返回类型。其余的参数按相同顺序从失败方法的参数列表中填充。

4.3、自定义@Retryable的行为

为了自定义重试的行为,我们可以使用参数maxAttemptsbackoff

  1. @Service
  2. public interface MyService {
  3. @Retryable( value = SQLException.class,
  4. maxAttempts = 2, backoff = @Backoff(delay = 100))
  5. void retryServiceWithCustomization(String sql) throws SQLException;
  6. }

这样最多将有两次尝试和100毫秒的延迟。

4.4、使用Spring Properties

我们还可以在@Retryable注解中使用properties。

为了演示这一点,我们将看到如何将delaymaxAttempts的值外部化到一个properties文件中。

首先,让我们在名为retryConfig.properties的文件中定义属性:

  1. retry.maxAttempts=2
  2. retry.maxDelay=100

然后我们指示@Configuration类加载这个文件:

  1. @PropertySource("classpath:retryConfig.properties")
  2. public class AppConfig { ... }
  3. // ...

最后,我们可以在@Retryable的定义中注入retry.maxAttemptsretry.maxDelay的值:

  1. @Service
  2. public interface MyService {
  3. @Retryable( value = SQLException.class, maxAttemptsExpression = "${retry.maxAttempts}",
  4. backoff = @Backoff(delayExpression = "${retry.maxDelay}"))
  5. void retryServiceWithExternalizedConfiguration(String sql) throws SQLException;
  6. }

请注意,我们现在使用的是maxAttemptsExpressiondelayExpression而不是maxAttemptsdelay

5、RetryTemplate

5.1、RetryOperations

Spring Retry提供了RetryOperations接口,它提供了一组execute()方法:

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

execute()方法的参数RetryCallback,是一个接口,可以插入需要在失败时重试的业务逻辑:

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

5.2、RetryTemplate配置

RetryTemplateRetryOperations的一个实现。

让我们在@Configuration类中配置一个RetryTemplate的bean:

  1. @Configuration
  2. public class AppConfig {
  3. //...
  4. @Bean
  5. public RetryTemplate retryTemplate() {
  6. RetryTemplate retryTemplate = new RetryTemplate();
  7. FixedBackOffPolicy fixedBackOffPolicy = new FixedBackOffPolicy();
  8. fixedBackOffPolicy.setBackOffPeriod(2000l);
  9. retryTemplate.setBackOffPolicy(fixedBackOffPolicy);
  10. SimpleRetryPolicy retryPolicy = new SimpleRetryPolicy();
  11. retryPolicy.setMaxAttempts(2);
  12. retryTemplate.setRetryPolicy(retryPolicy);
  13. return retryTemplate;
  14. }
  15. }

这个RetryPolicy确定了何时应该重试操作。

其中SimpleRetryPolicy定义了重试的固定次数,另一方面,BackOffPolicy用于控制重试尝试之间的回退。

最后,FixedBackOffPolicy会使重试在继续之前暂停一段固定的时间。

5.3、使用RetryTemplate

要使用重试处理来运行代码,我们可以调用retryTemplate.execute()方法:

  1. retryTemplate.execute(new RetryCallback<Void, RuntimeException>() {
  2. @Override
  3. public Void doWithRetry(RetryContext arg0) {
  4. myService.templateRetryService();
  5. ...
  6. }
  7. });

我们可以使用lambda表达式代替匿名类:

  1. retryTemplate.execute(arg0 -> {
  2. myService.templateRetryService();
  3. return null;
  4. });

6、监听器

监听器在重试时提供另外的回调。我们可以用这些来关注跨不同重试的各个横切点。

6.1、添加回调

回调在RetryListener接口中提供:

  1. public class DefaultListenerSupport extends RetryListenerSupport {
  2. @Override
  3. public <T, E extends Throwable> void close(RetryContext context,
  4. RetryCallback<T, E> callback, Throwable throwable) {
  5. logger.info("onClose");
  6. ...
  7. super.close(context, callback, throwable);
  8. }
  9. @Override
  10. public <T, E extends Throwable> void onError(RetryContext context,
  11. RetryCallback<T, E> callback, Throwable throwable) {
  12. logger.info("onError");
  13. ...
  14. super.onError(context, callback, throwable);
  15. }
  16. @Override
  17. public <T, E extends Throwable> boolean open(RetryContext context,
  18. RetryCallback<T, E> callback) {
  19. logger.info("onOpen");
  20. ...
  21. return super.open(context, callback);
  22. }
  23. }

openclose的回调在整个重试之前和之后执行,而onError应用于单个RetryCallback调用。

6.2、注册监听器

接下来,我们将我们的监听器(DefaultListenerSupport)注册到我们的RetryTemplate bean:

  1. @Configuration
  2. public class AppConfig {
  3. ...
  4. @Bean
  5. public RetryTemplate retryTemplate() {
  6. RetryTemplate retryTemplate = new RetryTemplate();
  7. ...
  8. retryTemplate.registerListener(new DefaultListenerSupport());
  9. return retryTemplate;
  10. }
  11. }

7、测试结果

为了完成我们的示例,让我们验证一下结果:

  1. @RunWith(SpringJUnit4ClassRunner.class)
  2. @ContextConfiguration(
  3. classes = AppConfig.class,
  4. loader = AnnotationConfigContextLoader.class)
  5. public class SpringRetryIntegrationTest {
  6. @Autowired
  7. private MyService myService;
  8. @Autowired
  9. private RetryTemplate retryTemplate;
  10. @Test(expected = RuntimeException.class)
  11. public void givenTemplateRetryService_whenCallWithException_thenRetry() {
  12. retryTemplate.execute(arg0 -> {
  13. myService.templateRetryService();
  14. return null;
  15. });
  16. }
  17. }

从测试日志中可以看出,我们已经正确配置了RetryTemplateRetryListener

  1. 2020-01-09 20:04:10 [main] INFO c.p.s.DefaultListenerSupport - onOpen
  2. 2020-01-09 20:04:10 [main] INFO c.pinmost.springretry.MyServiceImpl - throw RuntimeException in method templateRetryService()
  3. 2020-01-09 20:04:10 [main] INFO c.p.s.DefaultListenerSupport - onError
  4. 2020-01-09 20:04:12 [main] INFO c.pinmost.springretry.MyServiceImpl - throw RuntimeException in method templateRetryService()
  5. 2020-01-09 20:04:12 [main] INFO c.p.s.DefaultListenerSupport - onError
  6. 2020-01-09 20:04:12 [main] INFO c.p.s.DefaultListenerSupport - onClose

8、结论

在本文中,我们看到了如何使用注解、RetryTemplate和回调监听器来使用Spring Retry。

原文地址:https://www.baeldung.com/spring-retry

翻译:码农熊猫

更多技术干货,请访问我的个人网站https://pinmost.com,或关注公众号【码农熊猫】

Spring框架中一个有用的小组件:Spring Retry的更多相关文章

  1. spring框架中一个跟String的trim方法一样的方法

    @Test public void testTrimWhitespace() throws Exception { assertEquals(null, StringUtils.trimWhitesp ...

  2. 细说shiro之五:在spring框架中集成shiro

    官网:https://shiro.apache.org/ 1. 下载在Maven项目中的依赖配置如下: <!-- shiro配置 --> <dependency> <gr ...

  3. Spring5源码解析-Spring框架中的单例和原型bean

    Spring5源码解析-Spring框架中的单例和原型bean 最近一直有问我单例和原型bean的一些原理性问题,这里就开一篇来说说的 通过Spring中的依赖注入极大方便了我们的开发.在xml通过& ...

  4. Spring框架中IoC(控制反转)的原理(转)

    原文链接:Spring框架中IoC(控制反转)的原理 一.IoC的基础知识以及原理: 1.IoC理论的背景:在采用面向对象方法设计的软件系统中,底层实现都是由N个对象组成的,所有的对象通过彼此的合作, ...

  5. 漫谈 GOF 设计模式在 Spring 框架中的实现

    原文地址:梁桂钊的博客 博客地址:http://blog.720ui.com 欢迎关注公众号:「服务端思维」.一群同频者,一起成长,一起精进,打破认知的局限性. 漫谈 GOF 设计模式在 Spring ...

  6. 设计模式在 Spring 框架中的良好应用

    在开始正文之前,请你先思考几个问题: 你项目中有使用哪些 GOF 设计模式 说一说 GOF 23 种设计模式的设计理念 说说 Spring 框架中如何实现设计模式 假设我是面试官问起了你这些面试题,你 ...

  7. 再析在spring框架中解决多数据源的问题

    在前面我写了<如何在spring框架中解决多数据源的问题>,通过设计模式中的Decorator模式在spring框架中解决多数据源的问题,得到了许多网友的关注.在与网友探讨该问题的过程中, ...

  8. Spring框架中 配置c3p0连接池 完成对数据库的访问

    开发准备: 1.导入jar包: ioc基本jar jdbcTemplate基本jar c3p0基本jar 别忘了mysql数据库驱动jar 原始程序代码:不使用配置文件方式(IOC)生成访问数据库对象 ...

  9. Spring框架中ModelAndView、Model、ModelMap区别

    原文地址:http://www.cnblogs.com/google4y/p/3421017.html SPRING框架中ModelAndView.Model.ModelMap区别   注意:如果方法 ...

随机推荐

  1. Spring boot未授权访问造成的数据库外联

    一.spring boot 日常测试或攻防演练中像shiro,fastjson等漏洞已经越来越少了,但是随着spring boot框架的广泛使用,spring boot带来的安全问题也越来越多,本文仅 ...

  2. VLAN与三层交换机

    VLAN概述与优势 ①分割广播域 物理分割 逻辑分割 ②VLAN的优势 控制广播 增强网络安全性 简化网络管理 VLAN的范围 VlAN  ID范围 范围 用途 0,4095 保留 仅限系统使用,用户 ...

  3. Lombok——一款Java构建工具,“懒人”必备!!(idea版)

    一.简介 Lombok 是一种 Jav 构建工具,可用来帮助开发人员消除 Java 的冗长代码,尤其是对于简单的 Java 对象(POJO).它是通过注解实现这一目的. 二.使用 1.在idea中安装 ...

  4. 『居善地』接口测试 — 11、接口签名sign原理

    目录 1.什么是加密以及解密? 2.加密方式的分类 (1)对称加密 (2)非对称加密 (3)总结: 3.接口签名sign原理 (1)什么是接口签名? (2)为什么需要做接口签名 (3)接口签名的实践方 ...

  5. Kafka源码分析(三) - Server端 - 消息存储

    系列文章目录 https://zhuanlan.zhihu.com/p/367683572 目录 系列文章目录 一. 业务模型 1.1 概念梳理 1.2 文件分析 1.2.1 数据目录 1.2.2 . ...

  6. MySQL:数据库优化,看这篇就够了

    数据库优化一方面是找出系统的瓶颈,提高MySQL数据库的整体性能,而另一方面需要合理的结构设计和参数调整,以提高用户的相应速度,同时还要尽可能的节约系统资源,以便让系统提供更大的负荷. 1. 优化一览 ...

  7. 彻底解决Spring mvc中时间类型的转换和序列化问题

    在使用Spring mvc 进行开发时我们经常遇到前端传来的某种格式的时间字符串无法用java8时间包下的具体类型参数来直接接收.同时还有一系列的序列化 .反序列化问题,在返回前端带时间类型的同样会出 ...

  8. 如何基于MindSpore实现万亿级参数模型算法?

    摘要:近来,增大模型规模成为了提升模型性能的主要手段.特别是NLP领域的自监督预训练语言模型,规模越来越大,从GPT3的1750亿参数,到Switch Transformer的16000亿参数,又是一 ...

  9. Vue(12)组件的组织结构和组件注册

    组件的组织 通常一个应用会以一棵嵌套的组件树的形式来组织: 例如,你可能会有页头.侧边栏.内容区等组件,每个组件又包含了其它的像导航链接.博文之类的组件. 为了能在模板中使用,这些组件必须先注册以便 ...

  10. Flannel和Calico网络插件对比

    1.Kubernetes通信问题 1.容器间通信:即同一个Pod内多个容器间通信,通常使用loopback来实现. 2.Pod间通信:K8s要求,Pod和Pod之间通信必须使用Pod-IP 直接访问另 ...