一、RabbitMQ实现重试次数方法一-SpringRetry
一般来说 RabbitMQ有个方法 channel.basicNack()能够让消息回到队列中,这样可以实现重试。但是这样没有明确重试次数,如果当前的消息一直重试的话,则后面的消息就会堆积起来,导致后面的消息无法消费。这是一个致命的缺点。因此这就需要设置重试次数来解决这种问题。下面提供几种解决方案。
这一节介绍使用 spring-rabbit自带的 retry功能。
BackOffPolicy:重试的回退策略,指以何种方式进行下一次重试(第一次重试后什么时候进行第二次重试),比如过了15秒后重试,随机时间重试。
RetryPolicy:重试策略或条件,可以指定超时重试,一直重试,简单重试等。
MessageRecoverer:消息回收类,当所有的重试次数都失败后,就会调用该类的 recover方法
RetryTemplate:组合了 BackOffPolicy, RetryPolicy, RetryListener,执行重试步骤的具体类。
RetryOperationsInterceptor:方法执行失败的拦截器类,拦截失败后交给 RertyTemplate去执行重试。
SimpleMessageListenerContainer:用于管理消费者。
RetryListener:重试过程的监听器,第一次重试调用该类的 open,每次重试不成功调用 onError,最后一次重试调用 close。
设置消息确认模式为 auto, SpringBoot环境添加以下配置。
配置 SimpleMessageListenerContainer,并抛出异常重试。使用 simpleRabbitListenerContainerFactory创建 SimpleMessageListenerContainer有个好处就是在 application.properties中对 SimpleMessageListenerContainer的配置可以生效。比如配置 spring.rabbitmq.listener.simple.acknowledge-mode=auto就可以直接生效。
二、Spring Boot 一个注解搞定重试机制,不能太优雅了
在实际工作中,重处理是一个非常常见的场景,比如:
这些错误可能是因为网络波动造成的,等待过后重处理就能成功。通常来说,会用 try/catch, while循环之类的语法来进行重处理,但是这样的做法缺乏统一性,并且不是很方便,要多写很多代码。
然而 spring-retry却可以通过注解,在不入侵原有业务逻辑代码的方式下,优雅的实现重处理功能。
spring系列的 spring-retry是另一个实用程序模块,可以帮助我们以标准方式处理任何特定操作的重试。在 spring-retry中,所有配置都是基于简单注释的。
Spring Boot基础就不介绍了,推荐下这个实战教程:https://github.com/javastacks/spring-boot-best-practice
来简单解释一下注解中几个参数的含义:
当重试耗尽时还是失败,会出现什么情况呢?
当重试耗尽时, RetryOperations可以将控制传递给另一个回调,即 RecoveryCallback。 Spring-Retry还提供了@Recover注解,用于@Retryable重试失败后处理方法。如果不需要回调方法,可以直接不写回调方法,那么实现的效果是,重试次数完了后,如果还是没成功没符合业务判断,就抛出异常。
可以看到传参里面写的是 Exception e,这个是作为回调的接头暗号(重试次数用完了,还是失败,我们抛出这个 Exception e通知触发这个回调方法)。
对于@Recover注解的方法,需要特别注意的是:
本篇主要简单介绍了Springboot中的 Retryable的使用,主要的适用场景和注意事项,当需要重试的时候还是很有用的。
三、Spring Boot中使用Spring-Retry重试框架
在启动类中使用@EnableRetry注解
需要在重试的代码中加入重试注解@Retryable
默认情况下,会重试3次,间隔1秒
我们可以从注解@Retryable中看到
我们来运行测试代码
运行结果如下:
可以看到重新执行了3次 service1()方法,然后间隔是1秒,然后最后还是重试失败,所以抛出了异常
既然我们看到了注解@Retryable中有这么多参数可以设置,那我们就来介绍几个常用的配置。
首先是 maxAttempts,用于设置重试次数
从运行结果可以看到,方法执行了5次。
下面来介绍 maxAttemptsExpression的设置
maxAttemptsExpression则可以使用表达式,比如上述就是通过获取配置中maxAttempts的值,我们可以在application.yml设置。上述其实省略掉了SpEL表达式#{....},运行结果的话可以发现方法执行了4次..
我们可以使用SpEL表达式
接着我们下面来看看 exceptionExpression,一样也是写SpEL表达式
上面的表达式 exceptionExpression="message.contains('test')"的作用其实是获取到抛出来exception的message(调用了 getMessage()方法),然后判断message的内容里面是否包含了 test字符串,如果包含的话就会执行重试。所以如果调用方法的时候传入的参数 exceptionMessage中包含了 test字符串的话就会执行重试。
但这里值得注意的是, Spring Retry 1.2.5之后 exceptionExpression是可以省略掉#{...}
使用1.2.5之后的版本运行是没有问题的
但是如果使用1.2.5版本之前包括1.2.5版本的话,运行的时候会报错如下:
还可以在表达式中执行一个方法,前提是方法的类在spring容器中注册了,@retryService其实就是获取bean name为 retryService的bean,然后调用里面的 checkException方法,传入的参数为#root,它其实就是抛出来的exception对象。一样的也是可以省略#{...}
运行结果:
当然还有更多表达式的用法了...
下面再来看看另一个配置 exclude
这个 exclude属性可以帮我们排除一些我们不想重试的异常
最后我们来看看这个 backoff重试等待策略,默认使用@Backoff注解。
我们先来看看这个@Backoff的 value属性,用于设置重试间隔
运行结果可以看出来重试的间隔为2秒
接下来介绍@Backoff的 delay属性,它与 value属性不能共存,当 delay不设置的时候会去读 value属性设置的值,如果 delay设置的话则会忽略 value属性
运行结果可以看出,重试的时间间隔为500ms
接下来我们来看``@Backoff的 multiplier`的属性,指定延迟倍数,默认为0。
multiplier设置为2,则表示第一次重试间隔为2s,第二次为4秒,第三次为8s
运行结果如下:
接下来我们来看看这个@Backoff的 maxDelay属性,设置最大的重试间隔,当超过这个最大的重试间隔的时候,重试的间隔就等于 maxDelay的值
运行结果:
可以最后的最大重试间隔是5秒
当@Retryable方法重试失败之后,最后就会调用@Recover方法。用于@Retryable失败时的“兜底”处理方法。@Recover的方法必须要与@Retryable注解的方法保持一致,第一入参为要重试的异常,其他参数与@Retryable保持一致,返回值也要一样,否则无法执行!
测试方法
运行结果:
可以看到这些配置跟我们直接写注解的方式是差不多的,这里就不过多的介绍了。。
RetryOperations定义重试的API, RetryTemplate是API的模板模式实现,实现了重试和熔断。提供的API如下:
下面主要介绍一下RetryTemplate配置的时候,需要设置的重试策略和退避策略
RetryPolicy是一个接口,然后有很多具体的实现,我们先来看看它的接口中定义了什么方法
我们来看看他有什么具体的实现类
看一下退避策略,退避是指怎么去做下一次的重试,在这里其实就是等待多长时间。
listener可以监听重试,并执行对应的回调方法
使用如下:
自定义一个Listener
把listener设置到retryTemplate中
测试结果:
原文链接:Spring Retry在SpringBoot中的应用- ityml-博客园
文章分享到这里,希望我们关于spring-retry和Spring Boot中使用Spring-Retry重试框架的内容能够给您带来一些新的认识和思考。如果您还有其他问题,欢迎继续探索我们的网站或者与我们交流,我们将尽力为您提供满意的答案。