SpringBoot中使用SpringRetry重试框架
SpringRetry提供了自动重新调用失败的操作的功能。这在错误可能是暂时的(例如瞬时网络故障)的情况下很有用。从2。2。0版本开始,重试功能已从SpringBatch中撤出,成为一个独立的新库:SpringRetryMaven依赖dependencygroupIdorg。springframework。retrygroupIdspringretryartifactIddependency!alsoneedtoaddSpringAOPintoourprojectdependencygroupIdorg。springframeworkgroupIdspringaspectsartifactIddependency注解使用开启Retry功能
在启动类中使用EnableRetry注解packageorg。example;importorg。springframework。boot。SpringApplication;importorg。springframework。boot。autoconfigure。SpringBootApplication;importorg。springframework。retry。annotation。EnableRetry;SpringBootApplicationEnableRetrypublicclassRetryApp{publicstaticvoidmain(String〔〕args){SpringApplication。run(RetryApp。class,args);}}注解Retryable
需要在重试的代码中加入重试注解Retryablepackageorg。example;importlombok。extern。slf4j。Slf4j;importorg。springframework。retry。annotation。Backoff;importorg。springframework。retry。annotation。Recover;importorg。springframework。retry。annotation。Retryable;importorg。springframework。stereotype。Service;importjava。time。LocalDateTime;ServiceSlf4jpublicclassRetryService{Retryable(valueIllegalAccessException。class)publicvoidservice1()throwsIllegalAccessException{log。info(dosomething。。。{},LocalDateTime。now());thrownewIllegalAccessException(manualexception);}}
默认情况下,会重试3次,间隔1秒
我们可以从注解Retryable中看到Target({ElementType。METHOD,ElementType。TYPE})Retention(RetentionPolicy。RUNTIME)DocumentedpublicinterfaceRetryable{Retryinterceptorbeannametobeappliedforretryablemethod。Ismutuallyexclusivewithotherattributes。returntheretryinterceptorbeannameStringinterceptor()default;Exceptiontypesthatareretryable。Synonymforincludes()。Defaultstoempty(andifexcludesisalsoemptyallexceptionsareretried)。returnexceptiontypestoretryClasslt;?extendsThrowable〔〕value()default{};Exceptiontypesthatareretryable。Defaultstoempty(andifexcludesisalsoemptyallexceptionsareretried)。returnexceptiontypestoretryClasslt;?extendsThrowable〔〕include()default{};Exceptiontypesthatarenotretryable。Defaultstoempty(andifincludesisalsoemptyallexceptionsareretried)。Ifincludesisemptybutexcludesisnot,allnotexcludedexceptionsareretriedreturnexceptiontypesnottoretryClasslt;?extendsThrowable〔〕exclude()default{};Auniquelabelforstatisticsreporting。Ifnotprovidedthecallermaychoosetoignoreit,orprovideadefault。returnthelabelforthestatisticsStringlabel()default;Flagtosaythattheretryisstateful:i。e。exceptionsarerethrown,buttheretrypolicyisappliedwiththesamepolicytosubsequentinvocationswiththesamearguments。Iffalsethenretryableexceptionsarenotrethrown。returntrueifretryisstateful,defaultfalsebooleanstateful()defaultfalse;returnthemaximumnumberofattempts(includingthefirstfailure),defaultsto3intmaxAttempts()default3;默认重试次数3次returnanexpressionevaluatedtothemaximumnumberofattempts(includingthefirstfailure),defaultsto3Overrides{linkmaxAttempts()}。since1。2StringmaxAttemptsExpression()default;Specifythebackoffpropertiesforretryingthisoperation。Thedefaultisasimple{linkBackoff}specificationwithnopropertiesseeitsdocumentationfordefaults。returnabackoffspecificationBackoffbackoff()defaultBackoff();默认的重试中的退避策略Specifyanexpressiontobeevaluatedafterthe{codeSimpleRetryPolicy。canRetry()}returnstruecanbeusedtoconditionallysuppresstheretry。Onlyinvokedafteranexceptionisthrown。Therootobjectfortheevaluationisthelast{codeThrowable}。Otherbeansinthecontextcanbereferenced。Forexample:preclasscode{codemessage。contains(youcanretrythis)}。preandpreclasscode{codesomeBean。shouldRetry(root)}。prereturntheexpression。since1。2StringexceptionExpression()default;BeannamesofretrylistenerstouseinsteadofdefaultonesdefinedinSpringcontextreturnretrylistenersbeannamesString〔〕listeners()default{};}Target(ElementType。TYPE)Retention(RetentionPolicy。RUNTIME)DocumentedpublicinterfaceBackoff{Synonymfor{linkdelay()}。returnthedelayinmilliseconds(default1000)longvalue()default1000;默认的重试间隔1秒Acanonicalbackoffperiod。Usedasaninitialvalueintheexponentialcase,andasaminimumvalueintheuniformcase。returntheinitialorcanonicalbackoffperiodinmilliseconds(default1000)longdelay()default0;Themaximimumwait(inmilliseconds)betweenretries。Iflessthanthe{linkdelay()}thenthedefaultof{valueorg。springframework。retry。backoff。ExponentialBackOffPolicyDEFAULTMAXINTERVAL}isapplied。returnthemaximumdelaybetweenretries(default0ignored)longmaxDelay()default0;Ifpositive,thenusedasamultiplierforgeneratingthenextdelayforbackoff。returnamultipliertousetocalculatethenextbackoffdelay(default0ignored)doublemultiplier()default0;Anexpressionevaluatingtothecanonicalbackoffperiod。Usedasaninitialvalueintheexponentialcase,andasaminimumvalueintheuniformcase。Overrides{linkdelay()}。returntheinitialorcanonicalbackoffperiodinmilliseconds。since1。2StringdelayExpression()default;Anexpressionevaluatingtothemaximimumwait(inmilliseconds)betweenretries。Iflessthanthe{linkdelay()}thenthedefaultof{valueorg。springframework。retry。backoff。ExponentialBackOffPolicyDEFAULTMAXINTERVAL}isapplied。Overrides{linkmaxDelay()}returnthemaximumdelaybetweenretries(default0ignored)since1。2StringmaxDelayExpression()default;Evaluatestoavauleusedasamultiplierforgeneratingthenextdelayforbackoff。Overrides{linkmultiplier()}。returnamultiplierexpressiontousetocalculatethenextbackoffdelay(default0ignored)since1。2StringmultiplierExpression()default;Intheexponentialcase({linkmultiplier()}0)setthistotruetohavethebackoffdelaysrandomized,sothatthemaximumdelayismultipliertimesthepreviousdelayandthedistributionisuniformbetweenthetwovalues。returntheflagtosignalrandomizationisrequired(defaultfalse)booleanrandom()defaultfalse;}
我们来运行测试代码packageorg。example;importorg。junit。jupiter。api。Test;importorg。springframework。beans。factory。annotation。Autowired;importorg。springframework。boot。test。context。SpringBootTest;SpringBootTestclassRetryServiceTest{AutowiredprivateRetryServiceretryService;TestvoidtestService1()throwsIllegalAccessException{retryService。service1();}}
运行结果如下:2021010519:40:41。221INFO3548〔main〕org。example。RetryService:dosomething。。。20210105T19:40:41。2217633002021010519:40:42。224INFO3548〔main〕org。example。RetryService:dosomething。。。20210105T19:40:42。2244365002021010519:40:43。225INFO3548〔main〕org。example。RetryService:dosomething。。。20210105T19:40:43。225189300java。lang。IllegalAccessException:manualexceptionatorg。example。RetryService。service1(RetryService。java:19)atorg。example。RetryServiceFastClassBySpringCGLIBc0995ddb。invoke(generated)atorg。springframework。cglib。proxy。MethodProxy。invoke(MethodProxy。java:218)atorg。springframework。aop。framework。CglibAopProxyCglibMethodInvocation。invokeJoinpoint(CglibAopProxy。java:769)atorg。springframework。aop。framework。ReflectiveMethodInvocation。proceed(ReflectiveMethodInvocation。java:163)atorg。springframework。aop。framework。CglibAopProxyCglibMethodInvocation。proceed(CglibAopProxy。java:747)atorg。springframework。retry。interceptor。RetryOperationsInterceptor1。doWithRetry(RetryOperationsInterceptor。java:91)atorg。springframework。retry。support。RetryTemplate。doExecute(RetryTemplate。java:287)atorg。springframework。retry。support。RetryTemplate。execute(RetryTemplate。java:164)atorg。springframework。retry。interceptor。RetryOperationsInterceptor。invoke(RetryOperationsInterceptor。java:118)atorg。springframework。retry。annotation。AnnotationAwareRetryOperationsInterceptor。invoke(AnnotationAwareRetryOperationsInterceptor。java:153)atorg。springframework。aop。framework。ReflectiveMethodInvocation。proceed(ReflectiveMethodInvocation。java:186)atorg。springframework。aop。framework。CglibAopProxyCglibMethodInvocation。proceed(CglibAopProxy。java:747)atorg。springframework。aop。framework。CglibAopProxyDynamicAdvisedInterceptor。intercept(CglibAopProxy。java:689)atorg。example。RetryServiceEnhancerBySpringCGLIB499afa1d。service1(generated)atorg。example。RetryServiceTest。testService1(RetryServiceTest。java:16)atjava。basejdk。internal。reflect。NativeMethodAccessorImpl。invoke0(NativeMethod)atjava。basejdk。internal。reflect。NativeMethodAccessorImpl。invoke(NativeMethodAccessorImpl。java:62)atjava。basejdk。internal。reflect。DelegatingMethodAccessorImpl。invoke(DelegatingMethodAccessorImpl。java:43)atjava。basejava。lang。reflect。Method。invoke(Method。java:566)atorg。junit。platform。commons。util。ReflectionUtils。invokeMethod(ReflectionUtils。java:675)atorg。junit。jupiter。engine。execution。MethodInvocation。proceed(MethodInvocation。java:60)atorg。junit。jupiter。engine。execution。InvocationInterceptorChainValidatingInvocation。proceed(InvocationInterceptorChain。java:125)atorg。junit。jupiter。engine。extension。TimeoutExtension。intercept(TimeoutExtension。java:132)atorg。junit。jupiter。engine。extension。TimeoutExtension。interceptTestableMethod(TimeoutExtension。java:124)atorg。junit。jupiter。engine。extension。TimeoutExtension。interceptTestMethod(TimeoutExtension。java:74)atorg。junit。jupiter。engine。execution。ExecutableInvokerReflectiveInterceptorCall。lambdaofVoidMethod0(ExecutableInvoker。java:115)atorg。junit。jupiter。engine。execution。ExecutableInvoker。lambdainvoke0(ExecutableInvoker。java:105)atorg。junit。jupiter。engine。execution。InvocationInterceptorChainInterceptedInvocation。proceed(InvocationInterceptorChain。java:104)atorg。junit。jupiter。engine。execution。InvocationInterceptorChain。proceed(InvocationInterceptorChain。java:62)atorg。junit。jupiter。engine。execution。InvocationInterceptorChain。chainAndInvoke(InvocationInterceptorChain。java:43)atorg。junit。jupiter。engine。execution。InvocationInterceptorChain。invoke(InvocationInterceptorChain。java:35)atorg。junit。jupiter。engine。execution。ExecutableInvoker。invoke(ExecutableInvoker。java:104)atorg。junit。jupiter。engine。execution。ExecutableInvoker。invoke(ExecutableInvoker。java:98)atorg。junit。jupiter。engine。descriptor。TestMethodTestDescriptor。lambdainvokeTestMethod6(TestMethodTestDescriptor。java:202)atorg。junit。platform。engine。support。hierarchical。ThrowableCollector。execute(ThrowableCollector。java:73)atorg。junit。jupiter。engine。descriptor。TestMethodTestDescriptor。invokeTestMethod(TestMethodTestDescriptor。java:198)atorg。junit。jupiter。engine。descriptor。TestMethodTestDescriptor。execute(TestMethodTestDescriptor。java:135)atorg。junit。jupiter。engine。descriptor。TestMethodTestDescriptor。execute(TestMethodTestDescriptor。java:69)atorg。junit。platform。engine。support。hierarchical。NodeTestTask。lambdaexecuteRecursively5(NodeTestTask。java:135)atorg。junit。platform。engine。support。hierarchical。ThrowableCollector。execute(ThrowableCollector。java:73)atorg。junit。platform。engine。support。hierarchical。NodeTestTask。lambdaexecuteRecursively7(NodeTestTask。java:125)atorg。junit。platform。engine。support。hierarchical。Node。around(Node。java:135)atorg。junit。platform。engine。support。hierarchical。NodeTestTask。lambdaexecuteRecursively8(NodeTestTask。java:123)atorg。junit。platform。engine。support。hierarchical。ThrowableCollector。execute(ThrowableCollector。java:73)atorg。junit。platform。engine。support。hierarchical。NodeTestTask。executeRecursively(NodeTestTask。java:122)atorg。junit。platform。engine。support。hierarchical。NodeTestTask。execute(NodeTestTask。java:80)atjava。basejava。util。ArrayList。forEach(ArrayList。java:1540)atorg。junit。platform。engine。support。hierarchical。SameThreadHierarchicalTestExecutorService。invokeAll(SameThreadHierarchicalTestExecutorService。java:38)atorg。junit。platform。engine。support。hierarchical。NodeTestTask。lambdaexecuteRecursively5(NodeTestTask。java:139)atorg。junit。platform。engine。support。hierarchical。ThrowableCollector。execute(ThrowableCollector。java:73)atorg。junit。platform。engine。support。hierarchical。NodeTestTask。lambdaexecuteRecursively7(NodeTestTask。java:125)atorg。junit。platform。engine。support。hierarchical。Node。around(Node。java:135)atorg。junit。platform。engine。support。hierarchical。NodeTestTask。lambdaexecuteRecursively8(NodeTestTask。java:123)atorg。junit。platform。engine。support。hierarchical。ThrowableCollector。execute(ThrowableCollector。java:73)atorg。junit。platform。engine。support。hierarchical。NodeTestTask。executeRecursively(NodeTestTask。java:122)atorg。junit。platform。engine。support。hierarchical。NodeTestTask。execute(NodeTestTask。java:80)atjava。basejava。util。ArrayList。forEach(ArrayList。java:1540)atorg。junit。platform。engine。support。hierarchical。SameThreadHierarchicalTestExecutorService。invokeAll(SameThreadHierarchicalTestExecutorService。java:38)atorg。junit。platform。engine。support。hierarchical。NodeTestTask。lambdaexecuteRecursively5(NodeTestTask。java:139)atorg。junit。platform。engine。support。hierarchical。ThrowableCollector。execute(ThrowableCollector。java:73)atorg。junit。platform。engine。support。hierarchical。NodeTestTask。lambdaexecuteRecursively7(NodeTestTask。java:125)atorg。junit。platform。engine。support。hierarchical。Node。around(Node。java:135)atorg。junit。platform。engine。support。hierarchical。NodeTestTask。lambdaexecuteRecursively8(NodeTestTask。java:123)atorg。junit。platform。engine。support。hierarchical。ThrowableCollector。execute(ThrowableCollector。java:73)atorg。junit。platform。engine。support。hierarchical。NodeTestTask。executeRecursively(NodeTestTask。java:122)atorg。junit。platform。engine。support。hierarchical。NodeTestTask。execute(NodeTestTask。java:80)atorg。junit。platform。engine。support。hierarchical。SameThreadHierarchicalTestExecutorService。submit(SameThreadHierarchicalTestExecutorService。java:32)atorg。junit。platform。engine。support。hierarchical。HierarchicalTestExecutor。execute(HierarchicalTestExecutor。java:57)atorg。junit。platform。engine。support。hierarchical。HierarchicalTestEngine。execute(HierarchicalTestEngine。java:51)atorg。junit。platform。launcher。core。DefaultLauncher。execute(DefaultLauncher。java:229)atorg。junit。platform。launcher。core。DefaultLauncher。lambdaexecute6(DefaultLauncher。java:197)atorg。junit。platform。launcher。core。DefaultLauncher。withInterceptedStreams(DefaultLauncher。java:211)atorg。junit。platform。launcher。core。DefaultLauncher。execute(DefaultLauncher。java:191)atorg。junit。platform。launcher。core。DefaultLauncher。execute(DefaultLauncher。java:128)atcom。intellij。junit5。JUnit5IdeaTestRunner。startRunnerWithArgs(JUnit5IdeaTestRunner。java:71)atcom。intellij。rt。junit。IdeaTestRunnerRepeater。startRunnerWithArgs(IdeaTestRunner。java:33)atcom。intellij。rt。junit。JUnitStarter。prepareStreamsAndStart(JUnitStarter。java:220)atcom。intellij。rt。junit。JUnitStarter。main(JUnitStarter。java:53)
可以看到重新执行了3次service1()方法,然后间隔是1秒,然后最后还是重试失败,所以抛出了异常
既然我们看到了注解Retryable中有这么多参数可以设置,那我们就来介绍几个常用的配置。Retryable(includeIllegalAccessException。class,maxAttempts5)publicvoidservice2()throwsIllegalAccessException{log。info(dosomething。。。{},LocalDateTime。now());thrownewIllegalAccessException(manualexception);}
首先是maxAttempts,用于设置重试次数2021010609:30:11。263INFO15612〔main〕org。example。RetryService:dosomething。。。20210106T09:30:11。2636219002021010609:30:12。265INFO15612〔main〕org。example。RetryService:dosomething。。。20210106T09:30:12。2656291002021010609:30:13。265INFO15612〔main〕org。example。RetryService:dosomething。。。20210106T09:30:13。2657012021010609:30:14。266INFO15612〔main〕org。example。RetryService:dosomething。。。20210106T09:30:14。2667054002021010609:30:15。266INFO15612〔main〕org。example。RetryService:dosomething。。。20210106T09:30:15。266733200java。lang。IllegalAccessException:manualexception。。。。
从运行结果可以看到,方法执行了5次。
下面来介绍maxAttemptsExpression的设置Retryable(valueIllegalAccessException。class,maxAttemptsExpression{maxAttempts})publicvoidservice3()throwsIllegalAccessException{log。info(dosomething。。。{},LocalDateTime。now());thrownewIllegalAccessException(manualexception);}
maxAttemptsExpression则可以使用表达式,比如上述就是通过获取配置中maxAttempts的值,我们可以在application。yml设置。上述其实省略掉了SpEL表达式{。。。。},运行结果的话可以发现方法执行了4次。。maxAttempts:4
我们可以使用SpEL表达式Retryable(valueIllegalAccessException。class,maxAttemptsExpression{11})publicvoidservice31()throwsIllegalAccessException{log。info(dosomething。。。{},LocalDateTime。now());thrownewIllegalAccessException(manualexception);}Retryable(valueIllegalAccessException。class,maxAttemptsExpression{{maxAttempts}})效果和上面的一样publicvoidservice32()throwsIllegalAccessException{log。info(dosomething。。。{},LocalDateTime。now());thrownewIllegalAccessException(manualexception);}
接着我们下面来看看exceptionExpression,一样也是写SpEL表达式Retryable(valueIllegalAccessException。class,exceptionExpressionmessage。contains(test))publicvoidservice4(StringexceptionMessage)throwsIllegalAccessException{log。info(dosomething。。。{},LocalDateTime。now());thrownewIllegalAccessException(exceptionMessage);}Retryable(valueIllegalAccessException。class,exceptionExpression{message。contains(test)})publicvoidservice43(StringexceptionMessage)throwsIllegalAccessException{log。info(dosomething。。。{},LocalDateTime。now());thrownewIllegalAccessException(exceptionMessage);}
上面的表达式exceptionExpressionmessage。contains(test)的作用其实是获取到抛出来exception的message(调用了getMessage()方法),然后判断message的内容里面是否包含了test字符串,如果包含的话就会执行重试。所以如果调用方法的时候传入的参数exceptionMessage中包含了test字符串的话就会执行重试。
但这里值得注意的是,SpringRetry1。2。5之后exceptionExpression是可以省略掉{。。。}
SinceSpringRetry1。2。5,forexceptionExpression,templatedexpressions({。。。})aredeprecatedinfavorofsimpleexpressionstrings(message。contains(thiscanberetried))。
使用1。2。5之后的版本运行是没有问题的dependencygroupIdorg。springframework。retrygroupIdspringretryartifactIdversion1。3。0versiondependency
但是如果使用1。2。5版本之前包括1。2。5版本的话,运行的时候会报错如下:2021010609:52:45。209INFO23220〔main〕org。example。RetryService:dosomething。。。20210106T09:52:45。209178200org。springframework。expression。spel。SpelEvaluationException:EL1001E:Typeconversionproblem,cannotconvertfromjava。lang。Stringtojava。lang。Booleanatorg。springframework。expression。spel。support。StandardTypeConverter。convertValue(StandardTypeConverter。java:75)atorg。springframework。expression。common。ExpressionUtils。convertTypedValue(ExpressionUtils。java:57)atorg。springframework。expression。common。LiteralExpression。getValue(LiteralExpression。java:106)atorg。springframework。retry。policy。ExpressionRetryPolicy。canRetry(ExpressionRetryPolicy。java:113)atorg。springframework。retry。support。RetryTemplate。canRetry(RetryTemplate。java:375)atorg。springframework。retry。support。RetryTemplate。doExecute(RetryTemplate。java:304)atorg。springframework。retry。support。RetryTemplate。execute(RetryTemplate。java:164)atorg。springframework。retry。interceptor。RetryOperationsInterceptor。invoke(RetryOperationsInterceptor。java:118)atorg。springframework。retry。annotation。AnnotationAwareRetryOperationsInterceptor。invoke(AnnotationAwareRetryOperationsInterceptor。java:153)atorg。springframework。aop。framework。ReflectiveMethodInvocation。proceed(ReflectiveMethodInvocation。java:186)atorg。springframework。aop。framework。CglibAopProxyCglibMethodInvocation。proceed(CglibAopProxy。java:749)atorg。springframework。aop。framework。CglibAopProxyDynamicAdvisedInterceptor。intercept(CglibAopProxy。java:691)atorg。example。RetryServiceEnhancerBySpringCGLIBd321a75e。service4(generated)atorg。example。RetryServiceTest。testService42(RetryServiceTest。java:46)atjava。basejdk。internal。reflect。NativeMethodAccessorImpl。invoke0(NativeMethod)atjava。basejdk。internal。reflect。NativeMethodAccessorImpl。invoke(NativeMethodAccessorImpl。java:62)atjava。basejdk。internal。reflect。DelegatingMethodAccessorImpl。invoke(DelegatingMethodAccessorImpl。java:43)atjava。basejava。lang。reflect。Method。invoke(Method。java:566)atorg。junit。platform。commons。util。ReflectionUtils。invokeMethod(ReflectionUtils。java:686)atorg。junit。jupiter。engine。execution。MethodInvocation。proceed(MethodInvocation。java:60)atorg。junit。jupiter。engine。execution。InvocationInterceptorChainValidatingInvocation。proceed(InvocationInterceptorChain。java:131)atorg。junit。jupiter。engine。extension。TimeoutExtension。intercept(TimeoutExtension。java:149)atorg。junit。jupiter。engine。extension。TimeoutExtension。interceptTestableMethod(TimeoutExtension。java:140)atorg。junit。jupiter。engine。extension。TimeoutExtension。interceptTestMethod(TimeoutExtension。java:84)atorg。junit。jupiter。engine。execution。ExecutableInvokerReflectiveInterceptorCall。lambdaofVoidMethod0(ExecutableInvoker。java:115)atorg。junit。jupiter。engine。execution。ExecutableInvoker。lambdainvoke0(ExecutableInvoker。java:105)atorg。junit。jupiter。engine。execution。InvocationInterceptorChainInterceptedInvocation。proceed(InvocationInterceptorChain。java:106)atorg。junit。jupiter。engine。execution。InvocationInterceptorChain。proceed(InvocationInterceptorChain。java:64)atorg。junit。jupiter。engine。execution。InvocationInterceptorChain。chainAndInvoke(InvocationInterceptorChain。java:45)atorg。junit。jupiter。engine。execution。InvocationInterceptorChain。invoke(InvocationInterceptorChain。java:37)atorg。junit。jupiter。engine。execution。ExecutableInvoker。invoke(ExecutableInvoker。java:104)atorg。junit。jupiter。engine。execution。ExecutableInvoker。invoke(ExecutableInvoker。java:98)atorg。junit。jupiter。engine。descriptor。TestMethodTestDescriptor。lambdainvokeTestMethod6(TestMethodTestDescriptor。java:212)atorg。junit。platform。engine。support。hierarchical。ThrowableCollector。execute(ThrowableCollector。java:73)atorg。junit。jupiter。engine。descriptor。TestMethodTestDescriptor。invokeTestMethod(TestMethodTestDescriptor。java:208)atorg。junit。jupiter。engine。descriptor。TestMethodTestDescriptor。execute(TestMethodTestDescriptor。java:137)atorg。junit。jupiter。engine。descriptor。TestMethodTestDescriptor。execute(TestMethodTestDescriptor。java:71)atorg。junit。platform。engine。support。hierarchical。NodeTestTask。lambdaexecuteRecursively5(NodeTestTask。java:135)atorg。junit。platform。engine。support。hierarchical。ThrowableCollector。execute(ThrowableCollector。java:73)atorg。junit。platform。engine。support。hierarchical。NodeTestTask。lambdaexecuteRecursively7(NodeTestTask。java:125)atorg。junit。platform。engine。support。hierarchical。Node。around(Node。java:135)atorg。junit。platform。engine。support。hierarchical。NodeTestTask。lambdaexecuteRecursively8(NodeTestTask。java:123)atorg。junit。platform。engine。support。hierarchical。ThrowableCollector。execute(ThrowableCollector。java:73)atorg。junit。platform。engine。support。hierarchical。NodeTestTask。executeRecursively(NodeTestTask。java:122)atorg。junit。platform。engine。support。hierarchical。NodeTestTask。execute(NodeTestTask。java:80)atjava。basejava。util。ArrayList。forEach(ArrayList。java:1540)atorg。junit。platform。engine。support。hierarchical。SameThreadHierarchicalTestExecutorService。invokeAll(SameThreadHierarchicalTestExecutorService。java:38)atorg。junit。platform。engine。support。hierarchical。NodeTestTask。lambdaexecuteRecursively5(NodeTestTask。java:139)atorg。junit。platform。engine。support。hierarchical。ThrowableCollector。execute(ThrowableCollector。java:73)atorg。junit。platform。engine。support。hierarchical。NodeTestTask。lambdaexecuteRecursively7(NodeTestTask。java:125)atorg。junit。platform。engine。support。hierarchical。Node。around(Node。java:135)atorg。junit。platform。engine。support。hierarchical。NodeTestTask。lambdaexecuteRecursively8(NodeTestTask。java:123)atorg。junit。platform。engine。support。hierarchical。ThrowableCollector。execute(ThrowableCollector。java:73)atorg。junit。platform。engine。support。hierarchical。NodeTestTask。executeRecursively(NodeTestTask。java:122)atorg。junit。platform。engine。support。hierarchical。NodeTestTask。execute(NodeTestTask。java:80)atjava。basejava。util。ArrayList。forEach(ArrayList。java:1540)atorg。junit。platform。engine。support。hierarchical。SameThreadHierarchicalTestExecutorService。invokeAll(SameThreadHierarchicalTestExecutorService。java:38)atorg。junit。platform。engine。support。hierarchical。NodeTestTask。lambdaexecuteRecursively5(NodeTestTask。java:139)atorg。junit。platform。engine。support。hierarchical。ThrowableCollector。execute(ThrowableCollector。java:73)atorg。junit。platform。engine。support。hierarchical。NodeTestTask。lambdaexecuteRecursively7(NodeTestTask。java:125)atorg。junit。platform。engine。support。hierarchical。Node。around(Node。java:135)atorg。junit。platform。engine。support。hierarchical。NodeTestTask。lambdaexecuteRecursively8(NodeTestTask。java:123)atorg。junit。platform。engine。support。hierarchical。ThrowableCollector。execute(ThrowableCollector。java:73)atorg。junit。platform。engine。support。hierarchical。NodeTestTask。executeRecursively(NodeTestTask。java:122)atorg。junit。platform。engine。support。hierarchical。NodeTestTask。execute(NodeTestTask。java:80)atorg。junit。platform。engine。support。hierarchical。SameThreadHierarchicalTestExecutorService。submit(SameThreadHierarchicalTestExecutorService。java:32)atorg。junit。platform。engine。support。hierarchical。HierarchicalTestExecutor。execute(HierarchicalTestExecutor。java:57)atorg。junit。platform。engine。support。hierarchical。HierarchicalTestEngine。execute(HierarchicalTestEngine。java:51)atorg。junit。platform。launcher。core。DefaultLauncher。execute(DefaultLauncher。java:248)atorg。junit。platform。launcher。core。DefaultLauncher。lambdaexecute5(DefaultLauncher。java:211)atorg。junit。platform。launcher。core。DefaultLauncher。withInterceptedStreams(DefaultLauncher。java:226)atorg。junit。platform。launcher。core。DefaultLauncher。execute(DefaultLauncher。java:199)atorg。junit。platform。launcher。core。DefaultLauncher。execute(DefaultLauncher。java:132)atcom。intellij。junit5。JUnit5IdeaTestRunner。startRunnerWithArgs(JUnit5IdeaTestRunner。java:71)atcom。intellij。rt。junit。IdeaTestRunnerRepeater。startRunnerWithArgs(IdeaTestRunner。java:33)atcom。intellij。rt。junit。JUnitStarter。prepareStreamsAndStart(JUnitStarter。java:220)atcom。intellij。rt。junit。JUnitStarter。main(JUnitStarter。java:53)Causedby:org。springframework。core。convert。ConversionFailedException:Failedtoconvertfromtype〔java。lang。String〕totype〔java。lang。Boolean〕forvaluemessage。contains(test);nestedexceptionisjava。lang。IllegalArgumentException:Invalidbooleanvaluemessage。contains(test)atorg。springframework。core。convert。support。ConversionUtils。invokeConverter(ConversionUtils。java:47)atorg。springframework。core。convert。support。GenericConversionService。convert(GenericConversionService。java:191)atorg。springframework。expression。spel。support。StandardTypeConverter。convertValue(StandardTypeConverter。java:70)。。。76moreCausedby:java。lang。IllegalArgumentException:Invalidbooleanvaluemessage。contains(test)atorg。springframework。core。convert。support。StringToBooleanConverter。convert(StringToBooleanConverter。java:63)atorg。springframework。core。convert。support。StringToBooleanConverter。convert(StringToBooleanConverter。java:31)atorg。springframework。core。convert。support。GenericConversionServiceConverterAdapter。convert(GenericConversionService。java:385)atorg。springframework。core。convert。support。ConversionUtils。invokeConverter(ConversionUtils。java:41)。。。78more
还可以在表达式中执行一个方法,前提是方法的类在spring容器中注册了,retryService其实就是获取beanname为retryService的bean,然后调用里面的checkException方法,传入的参数为root,它其实就是抛出来的exception对象。一样的也是可以省略{。。。}Retryable(valueIllegalAccessException。class,exceptionExpression{retryService。checkException(root)})publicvoidservice5(StringexceptionMessage)throwsIllegalAccessException{log。info(dosomething。。。{},LocalDateTime。now());thrownewIllegalAccessException(exceptionMessage);}Retryable(valueIllegalAccessException。class,exceptionExpressionretryService。checkException(root))publicvoidservice51(StringexceptionMessage)throwsIllegalAccessException{log。info(dosomething。。。{},LocalDateTime。now());thrownewIllegalAccessException(exceptionMessage);}publicbooleancheckException(Exceptione){log。error(errormessage:{},e。getMessage());returntrue;返回true的话表明会执行重试,如果返回false则不会执行重试}
运行结果:2021010613:33:52。913INFO23052〔main〕org。example。RetryService:dosomething。。。20210106T13:33:52。9134042021010613:33:52。981ERROR23052〔main〕org。example。RetryService:errormessage:testmessage2021010613:33:53。990ERROR23052〔main〕org。example。RetryService:errormessage:testmessage2021010613:33:53。990INFO23052〔main〕org。example。RetryService:dosomething。。。20210106T13:33:53。9909474002021010613:33:53。990ERROR23052〔main〕org。example。RetryService:errormessage:testmessage2021010613:33:54。992ERROR23052〔main〕org。example。RetryService:errormessage:testmessage2021010613:33:54。992INFO23052〔main〕org。example。RetryService:dosomething。。。20210106T13:33:54。992342900
当然还有更多表达式的用法了。。。Retryable(exceptionExpression{rootinstanceofT(java。lang。IllegalAccessException)})判断exception的类型publicvoidservice52(StringexceptionMessage){log。info(dosomething。。。{},LocalDateTime。now());thrownewNullPointerException(exceptionMessage);}Retryable(exceptionExpressionrootinstanceofT(java。lang。IllegalAccessException))publicvoidservice53(StringexceptionMessage){log。info(dosomething。。。{},LocalDateTime。now());thrownewNullPointerException(exceptionMessage);}Retryable(exceptionExpressionmyMessage。contains(test))查看自定义的MyException中的myMessage的值是否包含test字符串publicvoidservice54(StringexceptionMessage)throwsMyException{log。info(dosomething。。。{},LocalDateTime。now());thrownewMyException(exceptionMessage);自定义的exception}Retryable(exceptionExpressionroot。myMessage。contains(test))和上面service54方法的效果一样publicvoidservice55(StringexceptionMessage)throwsMyException{log。info(dosomething。。。{},LocalDateTime。now());thrownewMyException(exceptionMessage);}packageorg。example;importlombok。Getter;importlombok。Setter;GetterSetterpublicclassMyExceptionextendsException{privateStringmyMessage;publicMyException(StringmyMessage){this。myMessagemyMessage;}}
下面再来看看另一个配置excludeRetryable(excludeMyException。class)publicvoidservice6(StringexceptionMessage)throwsMyException{log。info(dosomething。。。{},LocalDateTime。now());thrownewMyException(exceptionMessage);}
这个exclude属性可以帮我们排除一些我们不想重试的异常
最后我们来看看这个backoff重试等待策略,默认使用Backoff注解。
我们先来看看这个Backoff的value属性,用于设置重试间隔Retryable(valueIllegalAccessException。class,backoffBackoff(value2000))publicvoidservice7()throwsIllegalAccessException{log。info(dosomething。。。{},LocalDateTime。now());thrownewIllegalAccessException();}
运行结果可以看出来重试的间隔为2秒2021010614:47:38。036INFO21116〔main〕org。example。RetryService:dosomething。。。20210106T14:47:38。0367326002021010614:47:40。038INFO21116〔main〕org。example。RetryService:dosomething。。。20210106T14:47:40。0377536002021010614:47:42。046INFO21116〔main〕org。example。RetryService:dosomething。。。20210106T14:47:42。046642900java。lang。IllegalAccessExceptionatorg。example。RetryService。service7(RetryService。java:113)。。。
接下来介绍Backoff的delay属性,它与value属性不能共存,当delay不设置的时候会去读value属性设置的值,如果delay设置的话则会忽略value属性Retryable(valueIllegalAccessException。class,backoffBackoff(value2000,delay500))publicvoidservice8()throwsIllegalAccessException{log。info(dosomething。。。{},LocalDateTime。now());thrownewIllegalAccessException();}
运行结果可以看出,重试的时间间隔为500ms2021010615:22:42。271INFO13512〔main〕org。example。RetryService:dosomething。。。20210106T15:22:42。2715048002021010615:22:42。772INFO13512〔main〕org。example。RetryService:dosomething。。。20210106T15:22:42。7722349002021010615:22:43。273INFO13512〔main〕org。example。RetryService:dosomething。。。20210106T15:22:43。273246700java。lang。IllegalAccessExceptionatorg。example。RetryService。service8(RetryService。java:121)
接下来我们来看Backoff的multiplier的属性,指定延迟倍数,默认为0。Retryable(valueIllegalAccessException。class,maxAttempts4,backoffBackoff(delay2000,multiplier2))publicvoidservice9()throwsIllegalAccessException{log。info(dosomething。。。{},LocalDateTime。now());thrownewIllegalAccessException();}
multiplier设置为2,则表示第一次重试间隔为2s,第二次为4秒,第三次为8s
运行结果如下:2021010615:58:07。458INFO23640〔main〕org。example。RetryService:dosomething。。。20210106T15:58:07。4582455002021010615:58:09。478INFO23640〔main〕org。example。RetryService:dosomething。。。20210106T15:58:09。4786813002021010615:58:13。478INFO23640〔main〕org。example。RetryService:dosomething。。。20210106T15:58:13。4789219002021010615:58:21。489INFO23640〔main〕org。example。RetryService:dosomething。。。20210106T15:58:21。489240600java。lang。IllegalAccessExceptionatorg。example。RetryService。service9(RetryService。java:128)。。。
接下来我们来看看这个Backoff的maxDelay属性,设置最大的重试间隔,当超过这个最大的重试间隔的时候,重试的间隔就等于maxDelay的值Retryable(valueIllegalAccessException。class,maxAttempts4,backoffBackoff(delay2000,multiplier2,maxDelay5000))publicvoidservice10()throwsIllegalAccessException{log。info(dosomething。。。{},LocalDateTime。now());thrownewIllegalAccessException();}
运行结果:2021010616:12:37。377INFO5024〔main〕org。example。RetryService:dosomething。。。20210106T16:12:37。3776161002021010616:12:39。381INFO5024〔main〕org。example。RetryService:dosomething。。。20210106T16:12:39。3812994002021010616:12:43。382INFO5024〔main〕org。example。RetryService:dosomething。。。20210106T16:12:43。3821695002021010616:12:48。396INFO5024〔main〕org。example。RetryService:dosomething。。。20210106T16:12:48。396327600java。lang。IllegalAccessExceptionatorg。example。RetryService。service10(RetryService。java:135)
可以最后的最大重试间隔是5秒注解Recover
当Retryable方法重试失败之后,最后就会调用Recover方法。用于Retryable失败时的兜底处理方法。Recover的方法必须要与Retryable注解的方法保持一致,第一入参为要重试的异常,其他参数与Retryable保持一致,返回值也要一样,否则无法执行!Retryable(valueIllegalAccessException。class)publicvoidservice11()throwsIllegalAccessException{log。info(dosomething。。。{},LocalDateTime。now());thrownewIllegalAccessException();}Recoverpublicvoidrecover11(IllegalAccessExceptione){log。info(serviceretryafterRecover{},e。getMessage());}Retryable(valueArithmeticException。class)publicintservice12()throwsIllegalAccessException{log。info(dosomething。。。{},LocalDateTime。now());return10;}Recoverpublicintrecover12(ArithmeticExceptione){log。info(serviceretryafterRecover{},e。getMessage());return0;}Retryable(valueArithmeticException。class)publicintservice13(Stringmessage)throwsIllegalAccessException{log。info(dosomething。。。{},{},message,LocalDateTime。now());return10;}Recoverpublicintrecover13(ArithmeticExceptione,Stringmessage){log。info({},serviceretryafterRecover{},message,e。getMessage());return0;}注解CircuitBreaker
熔断模式:指在具体的重试机制下失败后打开断路器,过了一段时间,断路器进入半开状态,允许一个进入重试,若失败再次进入断路器,成功则关闭断路器,注解为CircuitBreaker,具体包括熔断打开时间、重置过期时间openTimeout时间范围内失败maxAttempts次数后,熔断打开resetTimeout时长CircuitBreaker(openTimeout1000,resetTimeout3000,valueNullPointerException。class)publicvoidcircuitBreaker(intnum){log。info(进入断路器方法num{},num);if(num8)return;Integernnull;System。err。println(1n);}Recoverpublicvoidrecover(NullPointerExceptione){log。info(serviceretryafterRecover{},e。getMessage());}
测试方法TestpublicvoidtestCircuitBreaker()throwsInterruptedException{System。err。println(尝试进入断路器方法,并触发异常。。。);retryService。circuitBreaker(1);retryService。circuitBreaker(1);retryService。circuitBreaker(9);retryService。circuitBreaker(9);System。err。println(在openTimeout1秒之内重试次数为2次,未达到触发熔断,断路器依然闭合。。。);TimeUnit。SECONDS。sleep(1);System。err。println(超过openTimeout1秒之后,因为未触发熔断,所以重试次数重置,可以正常访问。。。,继续重试3次方法。。。);retryService。circuitBreaker(1);retryService。circuitBreaker(1);retryService。circuitBreaker(1);System。err。println(在openTimeout1秒之内重试次数为3次,达到触发熔断,不会执行重试,只会执行恢复方法。。。);retryService。circuitBreaker(1);TimeUnit。SECONDS。sleep(2);retryService。circuitBreaker(9);TimeUnit。SECONDS。sleep(3);System。err。println(超过resetTimeout3秒之后,断路器重新闭合。。。,可以正常访问);retryService。circuitBreaker(9);retryService。circuitBreaker(9);retryService。circuitBreaker(9);retryService。circuitBreaker(9);retryService。circuitBreaker(9);}
运行结果:尝试进入断路器方法,并触发异常。。。2021010721:44:20。842INFO7464〔main〕org。example。RetryService:进入断路器方法num12021010721:44:20。844INFO7464〔main〕org。example。RetryService:serviceretryafterRecovernull2021010721:44:20。845INFO7464〔main〕org。example。RetryService:进入断路器方法num12021010721:44:20。845INFO7464〔main〕org。example。RetryService:serviceretryafterRecovernull2021010721:44:20。845INFO7464〔main〕org。example。RetryService:进入断路器方法num92021010721:44:20。845INFO7464〔main〕org。example。RetryService:进入断路器方法num9在openTimeout1秒之内重试次数为2次,未达到触发熔断,断路器依然闭合。。。超过openTimeout1秒之后,因为未触发熔断,所以重试次数重置,可以正常访问。。。,继续重试3次方法。。。2021010721:44:21。846INFO7464〔main〕org。example。RetryService:进入断路器方法num12021010721:44:21。847INFO7464〔main〕org。example。RetryService:serviceretryafterRecovernull2021010721:44:21。847INFO7464〔main〕org。example。RetryService:进入断路器方法num12021010721:44:21。847INFO7464〔main〕org。example。RetryService:serviceretryafterRecovernull2021010721:44:21。847INFO7464〔main〕org。example。RetryService:进入断路器方法num12021010721:44:21。848INFO7464〔main〕org。example。RetryService:serviceretryafterRecovernull在openTimeout1秒之内重试次数为3次,达到触发熔断,不会执行重试,只会执行恢复方法。。。2021010721:44:21。848INFO7464〔main〕org。example。RetryService:serviceretryafterRecovernull2021010721:44:23。853INFO7464〔main〕org。example。RetryService:serviceretryafterRecovernull超过resetTimeout3秒之后,断路器重新闭合。。。,可以正常访问2021010721:44:26。853INFO7464〔main〕org。example。RetryService:进入断路器方法num92021010721:44:26。854INFO7464〔main〕org。example。RetryService:进入断路器方法num92021010721:44:26。855INFO7464〔main〕org。example。RetryService:进入断路器方法num92021010721:44:26。855INFO7464〔main〕org。example。RetryService:进入断路器方法num92021010721:44:26。856INFO7464〔main〕org。example。RetryService:进入断路器方法num9RetryTemplateRetryTemplate配置packageorg。example;importorg。springframework。context。annotation。Bean;importorg。springframework。context。annotation。Configuration;importorg。springframework。retry。backoff。FixedBackOffPolicy;importorg。springframework。retry。policy。SimpleRetryPolicy;importorg。springframework。retry。support。RetryTemplate;ConfigurationpublicclassAppConfig{BeanpublicRetryTemplateretryTemplate(){RetryTemplateretryTemplatenewRetryTemplate();SimpleRetryPolicyretryPolicynewSimpleRetryPolicy();设置重试策略retryPolicy。setMaxAttempts(2);retryTemplate。setRetryPolicy(retryPolicy);FixedBackOffPolicyfixedBackOffPolicynewFixedBackOffPolicy();设置退避策略fixedBackOffPolicy。setBackOffPeriod(2000L);retryTemplate。setBackOffPolicy(fixedBackOffPolicy);returnretryTemplate;}}
可以看到这些配置跟我们直接写注解的方式是差不多的,这里就不过多的介绍了使用RetryTemplatepackageorg。example;importorg。junit。jupiter。api。Test;importorg。springframework。beans。factory。annotation。Autowired;importorg。springframework。boot。test。context。SpringBootTest;importorg。springframework。retry。RetryCallback;importorg。springframework。retry。RetryContext;importorg。springframework。retry。support。RetryTemplate;SpringBootTestpublicclassRetryTemplateTest{AutowiredprivateRetryTemplateretryTemplate;AutowiredprivateRetryTemplateServiceretryTemplateService;Testvoidtest1()throwsIllegalAccessException{retryTemplate。execute(newRetryCallbackObject,IllegalAccessException(){OverridepublicObjectdoWithRetry(RetryContextcontext)throwsIllegalAccessException{retryTemplateService。service1();returnnull;}});}Testvoidtest2()throwsIllegalAccessException{retryTemplate。execute(newRetryCallbackObject,IllegalAccessException(){OverridepublicObjectdoWithRetry(RetryContextcontext)throwsIllegalAccessException{retryTemplateService。service1();returnnull;}},newRecoveryCallbackObject(){OverridepublicObjectrecover(RetryContextcontext)throwsException{log。info(RecoveryCallback。。。。);returnnull;}});}}
RetryOperations定义重试的API,RetryTemplate是API的模板模式实现,实现了重试和熔断。提供的API如下:packageorg。springframework。retry;importorg。springframework。retry。support。DefaultRetryState;Definesthebasicsetofoperationsimplementedby{linkRetryOperations}toexecuteoperationswithconfigurableretrybehaviour。authorRobHarropauthorDaveSyerpublicinterfaceRetryOperations{Executethesupplied{linkRetryCallback}withtheconfiguredretrysemantics。Seeimplementationsforconfigurationdetails。paramTthereturnvalueparamretryCallbackthe{linkRetryCallback}paramEtheexceptiontothrowreturnthevaluereturnedbythe{linkRetryCallback}uponsuccessfulinvocation。throwsEany{linkException}raisedbythe{linkRetryCallback}uponunsuccessfulretry。throwsEtheexceptionthrownT,EextendsThrowableTexecute(RetryCallbackT,EretryCallback)throwsE;Executethesupplied{linkRetryCallback}withafallbackonexhaustedretrytothe{linkRecoveryCallback}。Seeimplementationsforconfigurationdetails。paramrecoveryCallbackthe{linkRecoveryCallback}paramretryCallbackthe{linkRetryCallback}{linkRecoveryCallback}uponparamTthetypetoreturnparamEthetypeoftheexceptionreturnthevaluereturnedbythe{linkRetryCallback}uponsuccessfulinvocation,andthatreturnedbythe{linkRecoveryCallback}otherwise。throwsEany{linkException}raisedbytheunsuccessfulretry。T,EextendsThrowableTexecute(RetryCallbackT,EretryCallback,RecoveryCallbackTrecoveryCallback)throwsE;Asimplestatefulretry。Executethesupplied{linkRetryCallback}withatargetobjectfortheattemptidentifiedbythe{linkDefaultRetryState}。Exceptionsthrownbythecallbackarealwayspropagatedimmediatelysothestateisrequiredtobeabletoidentifythepreviousattempt,ifthereisonehencethestateisrequired。Normalpatternswouldseethismethodbeingusedinsideatransaction,wherethecallbackmightinvalidatethetransactionifitfails。Seeimplementationsforconfigurationdetails。paramretryCallbackthe{linkRetryCallback}paramretryStatethe{linkRetryState}paramTthetypeofthereturnvalueparamEthetypeoftheexceptiontoreturnreturnthevaluereturnedbythe{linkRetryCallback}uponsuccessfulinvocation,andthatreturnedbythe{linkRecoveryCallback}otherwise。throwsEany{linkException}raisedbythe{linkRecoveryCallback}。throwsExhaustedRetryExceptionifthelastattemptforthisstatehasalreadybeenreachedT,EextendsThrowableTexecute(RetryCallbackT,EretryCallback,RetryStateretryState)throwsE,ExhaustedRetryException;Astatefulretrywitharecoverypath。Executethesupplied{linkRetryCallback}withafallbackonexhaustedretrytothe{linkRecoveryCallback}andatargetobjectfortheretryattemptidentifiedbythe{linkDefaultRetryState}。paramrecoveryCallbackthe{linkRecoveryCallback}paramretryStatethe{linkRetryState}paramretryCallbackthe{linkRetryCallback}paramTthereturnvaluetypeparamEtheexceptiontypeseeexecute(RetryCallback,RetryState)returnthevaluereturnedbythe{linkRetryCallback}uponsuccessfulinvocation,andthatreturnedbythe{linkRecoveryCallback}otherwise。throwsEany{linkException}raisedbythe{linkRecoveryCallback}uponunsuccessfulretry。T,EextendsThrowableTexecute(RetryCallbackT,EretryCallback,RecoveryCallbackTrecoveryCallback,RetryStateretryState)throwsE;}
下面主要介绍一下RetryTemplate配置的时候,需要设置的重试策略和退避策略RetryPolicy
RetryPolicy是一个接口,然后有很多具体的实现,我们先来看看它的接口中定义了什么方法packageorg。springframework。retry;importjava。io。Serializable;A{linkRetryPolicy}isresponsibleforallocatingandmanagingresourcesneededby{linkRetryOperations}。The{linkRetryPolicy}allowsretryoperationstobeawareoftheircontext。Contextcanbeinternaltotheretryframework,e。g。tosupportnestedretries。Contextcanalsobeexternal,andthe{linkRetryPolicy}providesauniformAPIforarangeofdifferentplatformsfortheexternalcontext。authorDaveSyerpublicinterfaceRetryPolicyextendsSerializable{paramcontextthecurrentretrystatusreturntrueiftheoperationcanproceedbooleancanRetry(RetryContextcontext);Acquireresourcesneededfortheretryoperation。Thecallbackispassedinsothatmarkerinterfacescanbeusedandamanagercancollaboratewiththecallbacktosetupsomestateinthestatustoken。paramparenttheparentcontextifweareinanestedretry。returna{linkRetryContext}objectspecifictothispolicy。RetryContextopen(RetryContextparent);paramcontextaretrystatuscreatedbythe{linkopen(RetryContext)}methodofthispolicy。voidclose(RetryContextcontext);Calledonceperretryattempt,afterthecallbackfails。paramcontextthecurrentstatusobject。paramthrowabletheexceptiontothrowvoidregisterThrowable(RetryContextcontext,Throwablethrowable);}
我们来看看他有什么具体的实现类SimpleRetryPolicy默认最多重试3次TimeoutRetryPolicy默认在1秒内失败都会重试ExpressionRetryPolicy符合表达式就会重试CircuitBreakerRetryPolicy增加了熔断的机制,如果不在熔断状态,则允许重试CompositeRetryPolicy可以组合多个重试策略NeverRetryPolicy从不重试(也是一种重试策略哈)AlwaysRetryPolicy总是重试等等。。。BackOffPolicy
看一下退避策略,退避是指怎么去做下一次的重试,在这里其实就是等待多长时间。FixedBackOffPolicy默认固定延迟1秒后执行下一次重试ExponentialBackOffPolicy指数递增延迟执行重试,默认初始0。1秒,系数是2,那么下次延迟0。2秒,再下次就是延迟0。4秒,如此类推,最大30秒。ExponentialRandomBackOffPolicy在上面那个策略上增加随机性UniformRandomBackOffPolicy这个跟上面的区别就是,上面的延迟会不停递增,这个只会在固定的区间随机StatelessBackOffPolicy这个说明是无状态的,所谓无状态就是对上次的退避无感知,从它下面的子类也能看出来等等。。。RetryListener
listener可以监听重试,并执行对应的回调方法packageorg。springframework。retry;Interfaceforlistenerthatcanbeusedtoaddbehaviourtoaretry。Implementationsof{linkRetryOperations}canchosetoissuecallbackstoaninterceptorduringtheretrylifecycle。authorDaveSyerpublicinterfaceRetryListener{Calledbeforethefirstattemptinaretry。Forinstance,implementerscansetupstatethatisneededbythepoliciesinthe{linkRetryOperations}。Thewholeretrycanbevetoedbyreturningfalsefromthismethod,inwhichcasea{linkTerminatedRetryException}willbethrown。paramTthetypeofobjectreturnedbythecallbackparamEthetypeofexceptionitdeclaresmaybethrownparamcontextthecurrent{linkRetryContext}。paramcallbackthecurrent{linkRetryCallback}。returntrueiftheretryshouldproceed。T,EextendsThrowablebooleanopen(RetryContextcontext,RetryCallbackT,Ecallback);Calledafterthefinalattempt(successfulornot)。Allowtheinterceptortocleanupanyresourceitisholdingbeforecontrolreturnstotheretrycaller。paramcontextthecurrent{linkRetryContext}。paramcallbackthecurrent{linkRetryCallback}。paramthrowablethelastexceptionthatwasthrownbythecallback。paramEtheexceptiontypeparamTthereturnvalueT,EextendsThrowablevoidclose(RetryContextcontext,RetryCallbackT,Ecallback,Throwablethrowable);Calledaftereveryunsuccessfulattemptataretry。paramcontextthecurrent{linkRetryContext}。paramcallbackthecurrent{linkRetryCallback}。paramthrowablethelastexceptionthatwasthrownbythecallback。paramTthereturnvalueparamEtheexceptiontothrowT,EextendsThrowablevoidonError(RetryContextcontext,RetryCallbackT,Ecallback,Throwablethrowable);}
使用如下:
自定义一个Listenerpackageorg。example;importlombok。extern。slf4j。Slf4j;importorg。springframework。retry。RetryCallback;importorg。springframework。retry。RetryContext;importorg。springframework。retry。listener。RetryListenerSupport;Slf4jpublicclassDefaultListenerSupportextendsRetryListenerSupport{OverridepublicT,EextendsThrowablevoidclose(RetryContextcontext,RetryCallbackT,Ecallback,Throwablethrowable){log。info(onClose);super。close(context,callback,throwable);}OverridepublicT,EextendsThrowablevoidonError(RetryContextcontext,RetryCallbackT,Ecallback,Throwablethrowable){log。info(onError);super。onError(context,callback,throwable);}OverridepublicT,EextendsThrowablebooleanopen(RetryContextcontext,RetryCallbackT,Ecallback){log。info(onOpen);returnsuper。open(context,callback);}}
把listener设置到retryTemplate中packageorg。example;importlombok。extern。slf4j。Slf4j;importorg。springframework。context。annotation。Bean;importorg。springframework。context。annotation。Configuration;importorg。springframework。retry。backoff。FixedBackOffPolicy;importorg。springframework。retry。policy。SimpleRetryPolicy;importorg。springframework。retry。support。RetryTemplate;ConfigurationSlf4jpublicclassAppConfig{BeanpublicRetryTemplateretryTemplate(){RetryTemplateretryTemplatenewRetryTemplate();SimpleRetryPolicyretryPolicynewSimpleRetryPolicy();设置重试策略retryPolicy。setMaxAttempts(2);retryTemplate。setRetryPolicy(retryPolicy);FixedBackOffPolicyfixedBackOffPolicynewFixedBackOffPolicy();设置退避策略fixedBackOffPolicy。setBackOffPeriod(2000L);retryTemplate。setBackOffPolicy(fixedBackOffPolicy);retryTemplate。registerListener(newDefaultListenerSupport());设置retryListenerreturnretryTemplate;}}
测试结果:2021010810:48:05。663INFO20956〔main〕org。example。DefaultListenerSupport:onOpen2021010810:48:05。663INFO20956〔main〕org。example。RetryTemplateService:dosomething。。。2021010810:48:05。663INFO20956〔main〕org。example。DefaultListenerSupport:onError2021010810:48:07。664INFO20956〔main〕org。example。RetryTemplateService:dosomething。。。2021010810:48:07。664INFO20956〔main〕org。example。DefaultListenerSupport:onError2021010810:48:07。664INFO20956〔main〕org。example。RetryTemplateTest:RecoveryCallback。。。。2021010810:48:07。664INFO20956〔main〕org。example。DefaultListenerSupport:onClose原文
原文链接:SpringRetry在SpringBoot中的应用ityml博客园
散文欣赏(小学生版)欣赏,在不同人的眼里会有不同的答案。有人说欣赏的定义就是被别人肯定;有人说欣赏的东西可以看出一个人的内涵修养hellip;hellip;欣赏这两个简单至极的汉字,在我眼里可以是……
华为手机压制了苹果手机的价格,也让手机市场的价格体系更合理华为手机的一大贡献,就是压制了苹果手机的价格,华为手机也让整个手机市场的价格体系更合理。有人却觉得并非如此,他们反而认为华为手机带高了手机价格,而其它某家手机品牌才是压低了手机……
我最好的朋友初中英语作文范文mybestfriendlindaismybestfriend。sheis15yearsold。sheisaprettygirlwitharoundfaceandtwob……
全民OLED普及之路CFORCE首发2999元重磅新品今年8月上旬,国际知名消费类电子及显示器品牌CFORCE发布了一款重磅新品【CFORCECF015Next洛神系列】。这是一款15。6寸的OLED移动便携式显示屏,作为CFOR……
ParallelsDesktop17亮点整理,7大改进与变化ParallelsDesktop17forMac已经正式推出,最新版本主要是针对M1Mac电脑、Windows11和macOSMonterey进行全面优化,在效能表现也比前代提……
淡淡的风景作文800字淡淡的云飘动着,淡淡的风吹动着,淡淡的湖水流动着,一切都是淡淡的,那样的宁静,如今,草木皆非,而她那淡淡的笑容成为我心中最美的风景。精致的四合院,黝黑的宅门,锃亮的门钹,……
笔尖之暖ldquo;明天就要考试了,你要好好复习,别分心。rdquo;妈妈还是一如既往的唠叨,不过我都知道,自从哥哥高考落榜之后,妈妈就开始把所有的注意力和心思都放在我的学习上,即使上……
今日科技热点苹果官网卖翻新机,只比新机便宜200块是的,苹果又开始卖翻新机了。早在今年4月份,苹果就卖过一次iPadAir的翻新机。虽然当时很多网友吐槽它翻新还卖那么贵,但似乎销量并未受到多大影响。这不,苹果……
留好这份清单iPad必装照片编辑软件几天前,我写了关于2021款iPadPro12。9的体验文章,在文章中提到了几款照片后期摄影App。在文章发布之后,便有朋友私信我,可否分享一些iPad上好用的照片后期处理软件……
iPhone12S渲染图曝光,首次引入屏幕指纹,刘海面积减小哈喽大家好,我是你们的老朋友小馨,每天都会给大家更新我的原创内容,据此前消息,苹果将会在今年秋季推出新一代手机产品iPhone12S。近日,有网友曝光了iPhone12S的最新……
家用净水器十大名牌正所谓病从口入,在日常生活中每个人都离不开喝水,水是生命之源,日常饮水对于身体的健康还是很重要的,家里的自来水经常会有异味,带有颜色,饮用起来很不放心。自来水厂出来的水都是按照……
吴亦凡全英文歌曲July歌词《July》歌词直白简单,搭配欧美范十足的都市电子嘻哈音律,再经由吴亦凡迷人的低音炮演绎,即便单曲循环N遍,也不过瘾!因为在聆听过程中,听者往往会随音乐释放自己,无压力地hig……
部编版三年级下册八单元观察感受作文百鸟朝凤很久很久以前,凤凰只是一只很不起眼的小鸟,羽毛也很平常,丝毫不象传说中的那般光彩夺目。但它有一个优点:它很勤劳,不像别的鸟那样吃饱了就知道玩,而是从早到晚忙个不停,将别的鸟扔掉……
我的梦想从小,我就有一个梦想mdash;mdash;当一名著名画家。因为我酷爱绘画,所以我想当画家。更因为我想为国争光!我知道当画家不容易,所以,我为这个伟大的梦想付出了许多努力!……
生命诚可贵责任价更高作文800字世界上有许多不得不做的事情,那就是责任。责任的存在,是上天留给世人的一种考验,它不是一个甜美的字眼,而是具有岩石般的冷峻。回首是春,俯首是秋,历史的年轮无休止的转动。穿越……
缝隙ldquo;喂!东子!rdquo;我走在家乡热闹的街道上,在推推挤挤的人群中漫无目的地走着,却听见了这样一句儿时极为熟悉的呼唤声。我感到无比的惊讶,这不是以前在这儿读幼儿园时伙……
致三水华侨中学的一封信范文尊敬的三水华侨中学领导、老师们:你们好!我们是华南师范大学2011级赴三水华侨中学教育实习队的全体队员。根据学校教学计划的要求,我们将于2014年9月15日至11月7日到……
给我一丝阳光作文嘿嘿别动我们会得救的!我们困在废墟中有二十几分钟了,可头顶的废墟却还未被人打开。你别睡,睡着了就醒不过来了!我敢说啊,我只数十下,我们马上就会获救的!我慢慢回忆十几个小时……
人民公园之旅作文300字【篇一:人民公园之旅】一个阳光明媚的周末,我和妈妈来到了人民公园。一进门,我就看见了一个瀑布。我正静静地欣赏着它。突然,一阵微风吹过,我就闻到了一阵清香,我循着香味……
胜利的滋味作文400字我有一个爱好,那就是下象棋。我下过很多次,时而胜,时而败,数不胜数,我几乎都忘了。可有一次比赛,使我终身难忘。那是一个中午,我又要迎来一次与象棋高手爸爸来对战了。我和爸爸……
玉龙雪山写雪景的作文500字暑假我和爸爸妈妈去云南旅游,一路上我们看到了很多美丽的风景:有风光秀丽的蝴蝶泉,有漂亮的阿诗玛,还有一个个美丽动人的传说,但是我最难忘的还是我们爬玉龙雪山的情景。玉龙雪山……
腊八粥飘香绵绵情谊长的作文在日常学习、工作或生活中,许多人都有过写作文的经历,对作文都不陌生吧,借助作文人们可以反映客观事物、表达思想感情、传递知识信息。还是对作文一筹莫展吗?以下是小编整理的腊八粥飘香……
南郭先生抹墙的寓言故事南郭先生抹墙是一个非常有意思的寓言故事,这个寓言故事告诉我们,不管做什么事情,都要合乎适宜。滥竽充数之后,南郭先生认真学习泥瓦工技术,会抹一手好墙,人家都赞扬他,他很高兴……
中秋征文中秋节中秋节是一个团圆的节日,是个开心的节日,因为大家可以聚在一起享受团聚的喜悦,还可以一边吃着美味的月饼一边欣赏美丽的月亮。中秋节称ldquo;月夕rdquo;,ldquo;八月节……