今天小编给大家分享一下SpringBoot中怎么使用SpringRetry重试框架的相关知识点,内容详细,逻辑清晰,相信大部分人都还太了解这方面的知识,所以分享这篇文章给大家参考一下,希望大家阅读完这篇文章后有所收获,下面我们一起来了解一下吧。Spring Retry提供了自动重新调用失败的操作的功能。这在错误可能是暂时的(例如瞬时网络故障)的情况下很有用。 从2.2.0版本开始,重试功能已从Spring Batch中撤出,成为一个独立的新库:Spring Retry在启动类中使用@EnableRetry注解需要在重试的代码中加入重试注解@Retryable
默认情况下,会重试3次,间隔1秒我们可以从注解@Retryable
中看到我们来运行测试代码运行结果如下:2021-01-05 19:40:41.221 INFO 3548 — [ main] org.example.RetryService : do something… 2021-01-05T19:40:41.221763300
2021-01-05 19:40:42.224 INFO 3548 — [ main] org.example.RetryService : do something… 2021-01-05T19:40:42.224436500
2021-01-05 19:40:43.225 INFO 3548 — [ main] org.example.RetryService : do something… 2021-01-05T19:40:43.225189300可以看到重新执行了3次service1()
方法,然后间隔是1秒,然后最后还是重试失败,所以抛出了异常既然我们看到了注解@Retryable
中有这么多参数可以设置,那我们就来介绍几个常用的配置。首先是maxAttempts
,用于设置重试次数2021-01-06 09:30:11.263 INFO 15612 — [ main] org.example.RetryService : do something… 2021-01-06T09:30:11.263621900
2021-01-06 09:30:12.265 INFO 15612 — [ main] org.example.RetryService : do something… 2021-01-06T09:30:12.265629100
2021-01-06 09:30:13.265 INFO 15612 — [ main] org.example.RetryService : do something… 2021-01-06T09:30:13.265701
2021-01-06 09:30:14.266 INFO 15612 — [ main] org.example.RetryService : do something… 2021-01-06T09:30:14.266705400
2021-01-06 09:30:15.266 INFO 15612 — [ main] org.example.RetryService : do something… 2021-01-06T09:30:15.266733200java.lang.IllegalAccessException: manual exception
….从运行结果可以看到,方法执行了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
是可以省略掉#{...}
Since Spring Retry 1.2.5, for exceptionExpression
, templated expressions (#{...}
) are deprecated in favor of simple expression strings (message.contains('this can be retried')
).使用1.2.5之后的版本运行是没有问题的但是如果使用1.2.5版本之前包括1.2.5版本的话,运行的时候会报错如下:2021-01-06 09:52:45.209 INFO 23220 — [ main] org.example.RetryService : do something… 2021-01-06T09:52:45.209178200org.springframework.expression.spel.SpelEvaluationException: EL1001E: Type conversion problem, cannot convert from java.lang.String to java.lang.Boolean
at org.springframework.expression.spel.support.StandardTypeConverter.convertValue(StandardTypeConverter.java:75)
at org.springframework.expression.common.ExpressionUtils.convertTypedValue(ExpressionUtils.java:57)
at org.springframework.expression.common.LiteralExpression.getValue(LiteralExpression.java:106)
at org.springframework.retry.policy.ExpressionRetryPolicy.canRetry(ExpressionRetryPolicy.java:113)
at org.springframework.retry.support.RetryTemplate.canRetry(RetryTemplate.java:375)
at org.springframework.retry.support.RetryTemplate.doExecute(RetryTemplate.java:304)
at org.springframework.retry.support.RetryTemplate.execute(RetryTemplate.java:164)
at org.springframework.retry.interceptor.RetryOperationsInterceptor.invoke(RetryOperationsInterceptor.java:118)
at org.springframework.retry.annotation.AnnotationAwareRetryOperationsInterceptor.invoke(AnnotationAwareRetryOperationsInterceptor.java:153)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186)
at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.proceed(CglibAopProxy.java:749)
at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:691)
at org.example.RetryService$$EnhancerBySpringCGLIB$$d321a75e.service4(
at org.example.RetryServiceTest.testService4_2(RetryServiceTest.java:46)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.base/java.lang.reflect.Method.invoke(Method.java:566)
at org.junit.platform.commons.util.ReflectionUtils.invokeMethod(ReflectionUtils.java:686)
at org.junit.jupiter.engine.execution.MethodInvocation.proceed(MethodInvocation.java:60)
at org.jun免费云主机域名it.jupiter.engine.execution.InvocationInterceptorChain$ValidatingInvocation.proceed(InvocationInterceptorChain.java:131)
at org.junit.jupiter.engine.extension.TimeoutExtension.intercept(TimeoutExtension.java:149)
at org.junit.jupiter.engine.extension.TimeoutExtension.interceptTestableMethod(TimeoutExtension.java:140)
at org.junit.jupiter.engine.extension.TimeoutExtension.interceptTestMethod(TimeoutExtension.java:84)
at org.junit.jupiter.engine.execution.ExecutableInvoker$ReflectiveInterceptorCall.lambda$ofVoidMethod$0(ExecutableInvoker.java:115)
at org.junit.jupiter.engine.execution.ExecutableInvoker.lambda$invoke$0(ExecutableInvoker.java:105)
at org.junit.jupiter.engine.execution.InvocationInterceptorChain$InterceptedInvocation.proceed(InvocationInterceptorChain.java:106)
at org.junit.jupiter.engine.execution.InvocationInterceptorChain.proceed(InvocationInterceptorChain.java:64)
at org.junit.jupiter.engine.execution.InvocationInterceptorChain.chainAndInvoke(InvocationInterceptorChain.java:45)
at org.junit.jupiter.engine.execution.InvocationInterceptorChain.invoke(InvocationInterceptorChain.java:37)
at org.junit.jupiter.engine.execution.ExecutableInvoker.invoke(ExecutableInvoker.java:104)
at org.junit.jupiter.engine.execution.ExecutableInvoker.invoke(ExecutableInvoker.java:98)
at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.lambda$invokeTestMethod$6(TestMethodTestDescriptor.java:212)
at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.invokeTestMethod(TestMethodTestDescriptor.java:208)
at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.execute(TestMethodTestDescriptor.java:137)
at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.execute(TestMethodTestDescriptor.java:71)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$5(NodeTestTask.java:135)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$7(NodeTestTask.java:125)
at org.junit.platform.engine.support.hierarchical.Node.around(Node.java:135)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$8(NodeTestTask.java:123)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:122)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:80)
at java.base/java.util.ArrayList.forEach(ArrayList.java:1540)
at org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.invokeAll(SameThreadHierarchicalTestExecutorService.java:38)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$5(NodeTestTask.java:139)
at org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.submit(SameThreadHierarchicalTestExecutorService.java:32)
at org.junit.platform.engine.support.hierarchical.HierarchicalTestExecutor.execute(HierarchicalTestExecutor.java:57)
at org.junit.platform.engine.support.hierarchical.HierarchicalTestEngine.execute(HierarchicalTestEngine.java:51)
at org.junit.platform.launcher.core.DefaultLauncher.execute(DefaultLauncher.java:248)
at org.junit.platform.launcher.core.DefaultLauncher.lambda$execute$5(DefaultLauncher.java:211)
at org.junit.platform.launcher.core.DefaultLauncher.withInterceptedStreams(DefaultLauncher.java:226)
at org.junit.platform.launcher.core.DefaultLauncher.execute(DefaultLauncher.java:199)
at org.junit.platform.launcher.core.DefaultLauncher.execute(DefaultLauncher.java:132)
at com.intellij.junit5.JUnit5IdeaTestRunner.startRunnerWithArgs(JUnit5IdeaTestRunner.java:71)
at com.intellij.rt.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:33)
at com.intellij.rt.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:220)
at com.intellij.rt.junit.JUnitStarter.main(JUnitStarter.java:53)
Caused by: org.springframework.core.convert.ConversionFailedException: Failed to convert from type [java.lang.String] to type [java.lang.Boolean] for value ‘message.contains(‘test’)’; nested exception is java.lang.IllegalArgumentException: Invalid boolean value ‘message.contains(‘test’)’
at org.springframework.core.convert.support.ConversionUtils.invokeConverter(ConversionUtils.java:47)
at org.springframework.core.convert.support.GenericConversionService.convert(GenericConversionService.java:191)
at org.springframework.expression.spel.support.StandardTypeConverter.convertValue(StandardTypeConverter.java:70)
… 76 more
Caused by: java.lang.IllegalArgumentException: Invalid boolean value ‘message.contains(‘test’)’
at org.springframework.core.convert.support.StringToBooleanConverter.convert(StringToBooleanConverter.java:63)
at org.springframework.core.convert.support.StringToBooleanConverter.convert(StringToBooleanConverter.java:31)
at org.springframework.core.convert.support.GenericConversionService$ConverterAdapter.convert(GenericConversionService.java:385)
at org.springframework.core.convert.support.ConversionUtils.invokeConverter(ConversionUtils.java:41)
… 78 more@retryService
其实就是获取bean name为retryService
的bean,然后调用里面的checkException
方法,传入的参数为#root
,它其实就是抛出来的exception对象。一样的也是可以省略#{...}
运行结果:2021-01-06 13:33:52.913 INFO 23052 — [ main] org.example.RetryService : do something… 2021-01-06T13:33:52.913404
2021-01-06 13:33:52.981 ERROR 23052 — [ main] org.example.RetryService : error message:test message
2021-01-06 13:33:53.990 ERROR 23052 — [ main] org.example.RetryService : error message:test message
2021-01-06 13:33:53.990 INFO 23052 — [ main] org.example.RetryService : do something… 2021-01-06T13:33:53.990947400
2021-01-06 13:33:53.990 ERROR 23052 — [ main] org.example.RetryService : error message:test message
2021-01-06 13:33:54.992 ERROR 23052 — [ main] org.example.RetryService : error message:test message
2021-01-06 13:33:54.992 INFO 23052 — [ main] org.example.RetryService : do something… 2021-01-06T13:33:54.992342900当然还有更多表达式的用法了…下面再来看看另一个配置exclude
这个exclude
属性可以帮我们排除一些我们不想重试的异常最后我们来看看这个backoff
重试等待策略, 默认使用@Backoff
注解。我们先来看看这个@Backoff
的value
属性,用于设置重试间隔运行结果可以看出来重试的间隔为2秒2021-01-06 14:47:38.036 INFO 21116 — [ main] org.example.RetryService : do something… 2021-01-06T14:47:38.036732600
2021-01-06 14:47:40.038 INFO 21116 — [ main] org.example.RetryService : do something… 2021-01-06T14:47:40.037753600
2021-01-06 14:47:42.046 INFO 21116 — [ main] org.example.RetryService : do something… 2021-01-06T14:47:42.046642900
java.lang.IllegalAccessException
at org.example.RetryService.service7(RetryService.java:113)
…接下来介绍@Backoff
的delay
属性,它与value
属性不能共存,当delay
不设置的时候会去读value
属性设置的值,如果delay
设置的话则会忽略value
属性运行结果可以看出,重试的时间间隔为500ms2021-01-06 15:22:42.271 INFO 13512 — [ main] org.example.RetryService : do something… 2021-01-06T15:22:42.271504800
2021-01-06 15:22:42.772 INFO 13512 — [ main] org.example.RetryService : do something… 2021-01-06T15:22:42.772234900
2021-01-06 15:22:43.273 INFO 13512 — [ main] org.example.RetryService : do something… 2021-01-06T15:22:43.273246700
java.lang.IllegalAccessException
at org.example.RetryService.service8(RetryService.java:121)接下来我们来看“@Backoff的
multiplier`的属性, 指定延迟倍数, 默认为0。multiplier
设置为2,则表示第一次重试间隔为2s,第二次为4秒,第三次为8s运行结果如下:2021-01-06 15:58:07.458 INFO 23640 — [ main] org.example.RetryService : do something… 2021-01-06T15:58:07.458245500
2021-01-06 15:58:09.478 INFO 23640 — [ main] org.example.RetryService : do something… 2021-01-06T15:58:09.478681300
2021-01-06 15:58:13.478 INFO 23640 — [ main] org.example.RetryService : do something… 2021-01-06T15:58:13.478921900
2021-01-06 15:58:21.489 INFO 23640 — [ main] org.example.RetryService : do something… 2021-01-06T15:58:21.489240600
java.lang.IllegalAccessException
at org.example.RetryService.service9(RetryService.java:128)
…接下来我们来看看这个@Backoff
的maxDelay
属性,设置最大的重试间隔,当超过这个最大的重试间隔的时候,重试的间隔就等于maxDelay
的值运行结果:2021-01-06 16:12:37.377 INFO 5024 — [ main] org.example.RetryService : do something… 2021-01-06T16:12:37.377616100
2021-01-06 16:12:39.381 INFO 5024 — [ main] org.example.RetryService : do something… 2021-01-06T16:12:39.381299400
2021-01-06 16:12:43.382 INFO 5024 — [ main] org.example.RetryService : do something… 2021-01-06T16:12:43.382169500
2021-01-06 16:12:48.396 INFO 5024 — [ main] org.example.RetryService : do something… 2021-01-06T16:12:48.396327600
java.lang.IllegalAccessException
at org.example.RetryService.service10(RetryService.java:135)可以最后的最大重试间隔是5秒当@Retryable
方法重试失败之后,最后就会调用@Recover
方法。用于@Retryable
失败时的“兜底”处理方法。 @Recover
的方法必须要与@Retryable
注解的方法保持一致,第一入参为要重试的异常,其他参数与@Retryable
保持一致,返回值也要一样,否则无法执行!熔断模式:指在具体的重试机制下失败后打开断路器,过了一段时间,断路器进入半开状态,允许一个进入重试,若失败再次进入断路器,成功则关闭断路器,注解为@CircuitBreaker
,具体包括熔断打开时间、重置过期时间测试方法运行结果:尝试进入断路器方法,并触发异常…
2021-01-07 21:44:20.842 INFO 7464 — [ main] org.example.RetryService : 进入断路器方法num=1
2021-01-07 21:44:20.844 INFO 7464 — [ main] org.example.RetryService : service retry after Recover => null
2021-01-07 21:44:20.845 INFO 7464 — [ main] org.example.RetryService : 进入断路器方法num=1
2021-01-07 21:44:20.845 INFO 7464 — [ main] org.example.RetryService : service retry after Recover => null
2021-01-07 21:44:20.845 INFO 7464 — [ main] org.example.RetryService : 进入断路器方法num=9
2021-01-07 21:44:20.845 INFO 7464 — [ main] org.example.RetryService : 进入断路器方法num=9
在openTimeout 1秒之内重试次数为2次,未达到触发熔断, 断路器依然闭合…
超过openTimeout 1秒之后, 因为未触发熔断,所以重试次数重置,可以正常访问…,继续重试3次方法…
2021-01-07 21:44:21.846 INFO 7464 — [ main] org.example.RetryService : 进入断路器方法num=1
2021-01-07 21:44:21.847 INFO 7464 — [ main] org.example.RetryService : service retry after Recover => null
2021-01-07 21:44:21.847 INFO 7464 — [ main] org.example.RetryService : 进入断路器方法num=1
2021-01-07 21:44:21.847 INFO 7464 — [ main] org.example.RetryService : service retry after Recover => null
2021-01-07 21:44:21.847 INFO 7464 — [ main] org.example.RetryService : 进入断路器方法num=1
2021-01-07 21:44:21.848 INFO 7464 — [ main] org.example.RetryService : service retry after Recover => null
在openTimeout 1秒之内重试次数为3次,达到触发熔断,不会执行重试,只会执行恢复方法…
2021-01-07 21:44:21.848 INFO 7464 — [ main] org.example.RetryService : service retry after Recover => null
2021-01-07 21:44:23.853 INFO 7464 — [ main] org.example.RetryService : service retry after Recover => null
超过resetTimeout 3秒之后,断路器重新闭合…,可以正常访问
2021-01-07 21:44:26.853 INFO 7464 — [ main] org.example.RetryService : 进入断路器方法num=9
2021-01-07 21:44:26.854 INFO 7464 — [ main] org.example.RetryService : 进入断路器方法num=9
2021-01-07 21:44:26.855 INFO 7464 — [ main] org.example.RetryService : 进入断路器方法num=9
2021-01-07 21:44:26.855 INFO 7464 — [ main] org.example.RetryService : 进入断路器方法num=9
2021-01-07 21:44:26.856 INFO 7464 — [ main] org.example.RetryService : 进入断路器方法num=9可以看到这些配置跟我们直接写注解的方式是差不多的,这里就不过多的介绍了。。RetryOperations
定义重试的API,RetryTemplate
是API的模板模式实现,实现了重试和熔断。提供的API如下:下面主要介绍一下RetryTemplate配置的时候,需要设置的重试策略和退避策略RetryPolicy是一个接口, 然后有很多具体的实现,我们先来看看它的接口中定义了什么方法我们来看看他有什么具体的实现类SimpleRetryPolicy 默认最多重试3次TimeoutRetryPolicy 默认在1秒内失败都会重试ExpressionRetryPolicy 符合表达式就会重试CircuitBreakerRetryPolicy 增加了熔断的机制,如果不在熔断状态,则允许重试CompositeRetryPolicy 可以组合多个重试策略NeverRetryPolicy 从不重试(也是一种重试策略哈)AlwaysRetryPolicy 总是重试等等…看一下退避策略,退避是指怎么去做下一次的重试,在这里其实就是等待多长时间。FixedBackOffPolicy 默认固定延迟1秒后执行下一次重试ExponentialBackOffPolicy 指数递增延迟执行重试,默认初始0.1秒,系数是2,那么下次延迟0.2秒,再下次就是延迟0.4秒,如此类推,最大30秒。ExponentialRandomBackOffPolicy 在上面那个策略上增加随机性UniformRandomBackOffPolicy 这个跟上面的区别就是,上面的延迟会不停递增,这个只会在固定的区间随机StatelessBackOffPolicy 这个说明是无状态的,所谓无状态就是对上次的退避无感知,从它下面的子类也能看出来等等…listener可以监听重试,并执行对应的回调方法使用如下:自定义一个Listener把listener设置到retryTemplate中测试结果:2021-01-08 10:48:05.663 INFO 20956 — [ main] org.example.DefaultListenerSupport : onOpen
2021-01-08 10:48:05.663 INFO 20956 — [ main] org.example.RetryTemplateService : do something…
2021-01-08 10:48:05.663 INFO 20956 — [ main] org.example.DefaultListenerSupport : onError
2021-01-08 10:48:07.664 INFO 20956 — [ main] org.example.RetryTemplateService : do something…
2021-01-08 10:48:07.664 INFO 20956 — [ main] org.example.DefaultListenerSupport : onError
2021-01-08 10:48:07.664 INFO 20956 — [ main] org.example.RetryTemplateTest : RecoveryCallback….
2021-01-08 10:48:07.664 INFO 20956 — [ main] org.example.DefaultListenerSupport : onClose以上就是“SpringBoot中怎么使用SpringRetry重试框架”这篇文章的所有内容,感谢各位的阅读!相信大家阅读完这篇文章都有很大的收获,小编每天都会为大家更新不同的知识,如果还想学习更多的知识,请关注百云主机行业资讯频道。
相关推荐: laravel5中如何隐藏index.php后缀
这篇文章主要介绍“laravel5中如何隐藏index.php后缀”,在日常操作中,相信很多人在laravel5中如何隐藏index.php后缀问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”laravel5中如何隐藏index.…
免责声明:本站发布的图片视频文字,以转载和分享为主,文章观点不代表本站立场,本站不承担相关法律责任;如果涉及侵权请联系邮箱:360163164@qq.com举报,并提供相关证据,经查实将立刻删除涉嫌侵权内容。