广告
返回顶部
首页 > 资讯 > 精选 >Spring Boot中怎么使用Spring-Retry重试框架
  • 748
分享到

Spring Boot中怎么使用Spring-Retry重试框架

2023-06-30 09:06:47 748人浏览 独家记忆
摘要

这篇文章主要介绍“spring Boot中怎么使用Spring-Retry重试框架”,在日常操作中,相信很多人在Spring Boot中怎么使用Spring-Retry重试框架问题上存在疑惑,小编查阅了各式资料,整理出简

这篇文章主要介绍“spring Boot中怎么使用Spring-Retry重试框架”,在日常操作中,相信很多人在Spring Boot中怎么使用Spring-Retry重试框架问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”Spring Boot中怎么使用Spring-Retry重试框架”的疑惑有所帮助!接下来,请跟着小编一起来学习吧!

    Spring Retry提供了自动重新调用失败的操作的功能。这在错误可能是暂时的(例如瞬时网络故障)的情况下很有用。 从2.2.0版本开始,重试功能已从Spring Batch中撤出,成为一个独立的新库:Spring Retry

    Maven依赖

    <dependency>    <groupId>org.springframework.retry</groupId>    <artifactId>spring-retry</artifactId></dependency><!-- also need to add Spring aop into our project--><dependency>    <groupId>org.springframework</groupId>    <artifactId>spring-aspects</artifactId></dependency>

    注解使用

    开启Retry功能

    在启动类中使用@EnableRetry注解

    package org.example;import org.springframework.boot.SpringApplication;import org.springframework.boot.autoconfigure.SpringBootApplication;import org.springframework.retry.annotation.EnableRetry;@SpringBootApplication@EnableRetrypublic class RetryApp {    public static void main(String[] args) {        SpringApplication.run(RetryApp.class, args);    }}

    注解@Retryable

    需要在重试的代码中加入重试注解@Retryable

    package org.example;import lombok.extern.slf4j.Slf4j;import org.springframework.retry.annotation.Backoff;import org.springframework.retry.annotation.Recover;import org.springframework.retry.annotation.Retryable;import org.springframework.stereotype.Service;import java.time.LocalDateTime;@Service@Slf4jpublic class RetryService {    @Retryable(value = IllegalAccessException.class)    public void service1() throws IllegalAccessException {        log.info("do something... {}", LocalDateTime.now());        throw new IllegalAccessException("manual exception");    }}

    默认情况下,会重试3次,间隔1秒

    我们可以从注解@Retryable中看到

    @Target({ ElementType.METHOD, ElementType.TYPE })@Retention(RetentionPolicy.RUNTIME)@Documentedpublic @interface Retryable {String interceptor() default "";Class<? extends Throwable>[] value() default {};Class<? extends Throwable>[] include() default {};Class<? extends Throwable>[] exclude() default {};String label() default "";boolean stateful() default false;int maxAttempts() default 3;  //默认重试次数3次String maxAttemptsExpression() default "";Backoff backoff() default @Backoff(); //默认的重试中的退避策略String exceptionExpression() default "";String[] listeners() default {};}
    @Target(ElementType.TYPE)@Retention(RetentionPolicy.RUNTIME)@Documentedpublic @interface Backoff {long value() default 1000; //默认的重试间隔1秒long delay() default 0;long maxDelay() default 0;double multiplier() default 0;String delayExpression() default "";String maxDelayExpression() default "";String multiplierExpression() default "";boolean random() default false;}

    我们来运行测试代码

    package org.example;import org.junit.jupiter.api.Test;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.boot.test.context.SpringBootTest;@SpringBootTestclass RetryServiceTest {    @Autowired    private RetryService retryService;    @Test    void testService1() throws IllegalAccessException {        retryService.service1();    }}

    运行结果如下:

    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


    java.lang.IllegalAccessException: manual exception

        at org.example.RetryService.service1(RetryService.java:19)
        at org.example.RetryService$$FastClassBySprinGCGLIB$$c0995ddb.invoke(<generated>)
        at org.springframework.cglib.proxy.MethodProxy.invoke(MethodProxy.java:218)
        at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.invokeJoinpoint(CglibAopProxy.java:769)
        at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:163)
        at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.proceed(CglibAopProxy.java:747)
        at org.springframework.retry.interceptor.RetryOperationsInterceptor$1.doWithRetry(RetryOperationsInterceptor.java:91)
        at org.springframework.retry.support.RetryTemplate.doExecute(RetryTemplate.java:287)
        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:747)
        at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:689)
        at org.example.RetryService$$EnhancerBySpringCGLIB$$499afa1d.service1(<generated>)
        at org.example.RetryServiceTest.testService1(RetryServiceTest.java:16)
        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:675)
        at org.junit.jupiter.engine.execution.MethodInvocation.proceed(MethodInvocation.java:60)
        at org.junit.jupiter.engine.execution.InvocationInterceptorChain$ValidatingInvocation.proceed(InvocationInterceptorChain.java:125)
        at org.junit.jupiter.engine.extension.TimeoutExtension.intercept(TimeoutExtension.java:132)
        at org.junit.jupiter.engine.extension.TimeoutExtension.interceptTestableMethod(TimeoutExtension.java:124)
        at org.junit.jupiter.engine.extension.TimeoutExtension.interceptTestMethod(TimeoutExtension.java:74)
        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:104)
        at org.junit.jupiter.engine.execution.InvocationInterceptorChain.proceed(InvocationInterceptorChain.java:62)
        at org.junit.jupiter.engine.execution.InvocationInterceptorChain.chainAndInvoke(InvocationInterceptorChain.java:43)
        at org.junit.jupiter.engine.execution.InvocationInterceptorChain.invoke(InvocationInterceptorChain.java:35)
        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:202)
        at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
        at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.invokeTestMethod(TestMethodTestDescriptor.java:198)
        at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.execute(TestMethodTestDescriptor.java:135)
        at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.execute(TestMethodTestDescriptor.java:69)
        at org.junit.platform.engine.support.hierarchical.nodeTestTask.lambda$executeRecursively$5(NodeTestTask.java:135)
        at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
        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.ThrowableCollector.execute(ThrowableCollector.java:73)
        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.ThrowableCollector.execute(ThrowableCollector.java:73)
        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.ThrowableCollector.execute(ThrowableCollector.java:73)
        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.ThrowableCollector.execute(ThrowableCollector.java:73)
        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.ThrowableCollector.execute(ThrowableCollector.java:73)
        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 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:229)
        at org.junit.platform.launcher.core.DefaultLauncher.lambda$execute$6(DefaultLauncher.java:197)
        at org.junit.platform.launcher.core.DefaultLauncher.withInterceptedStreams(DefaultLauncher.java:211)
        at org.junit.platform.launcher.core.DefaultLauncher.execute(DefaultLauncher.java:191)
        at org.junit.platform.launcher.core.DefaultLauncher.execute(DefaultLauncher.java:128)
        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)

    可以看到重新执行了3次service1()方法,然后间隔是1秒,然后最后还是重试失败,所以抛出了异常

    既然我们看到了注解@Retryable中有这么多参数可以设置,那我们就来介绍几个常用的配置。

    @Retryable(include = IllegalAccessException.class, maxAttempts = 5)public void service2() throws IllegalAccessException {    log.info("do something... {}", LocalDateTime.now());    throw new IllegalAccessException("manual exception");}

    首先是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.266733200

    java.lang.IllegalAccessException: manual exception
    ....

    从运行结果可以看到,方法执行了5次。

    下面来介绍maxAttemptsExpression的设置

    @Retryable(value = IllegalAccessException.class, maxAttemptsExpression = "${maxAttempts}")public void service3() throws IllegalAccessException {    log.info("do something... {}", LocalDateTime.now());    throw new IllegalAccessException("manual exception");}

    maxAttemptsExpression则可以使用表达式,比如上述就是通过获取配置中maxAttempts的值,我们可以在application.yml设置。上述其实省略掉了SpEL表达式#{....},运行结果的话可以发现方法执行了4次..

    maxAttempts: 4

    我们可以使用SpEL表达式

    @Retryable(value = IllegalAccessException.class, maxAttemptsExpression = "#{1+1}")public void service3_1() throws IllegalAccessException {    log.info("do something... {}", LocalDateTime.now());    throw new IllegalAccessException("manual exception");}@Retryable(value = IllegalAccessException.class, maxAttemptsExpression = "#{${maxAttempts}}")//效果和上面的一样public void service3_2() throws IllegalAccessException {    log.info("do something... {}", LocalDateTime.now());    throw new IllegalAccessException("manual exception");}

    接着我们下面来看看exceptionExpression, 一样也是写SpEL表达式

    @Retryable(value = IllegalAccessException.class, exceptionExpression = "message.contains('test')")public void service4(String exceptionMessage) throws IllegalAccessException {    log.info("do something... {}", LocalDateTime.now());    throw new IllegalAccessException(exceptionMessage);}    @Retryable(value = IllegalAccessException.class, exceptionExpression = "#{message.contains('test')}")public void service4_3(String exceptionMessage) throws IllegalAccessException {    log.info("do something... {}", LocalDateTime.now());    throw new IllegalAccessException(exceptionMessage);}

    上面的表达式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之后的版本运行是没有问题的

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

    但是如果使用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.209178200

    org.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(<generated>)
        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.junit.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.ThrowableCollector.execute(ThrowableCollector.java:73)
        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.ThrowableCollector.execute(ThrowableCollector.java:73)
        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.ThrowableCollector.execute(ThrowableCollector.java:73)
        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.ThrowableCollector.execute(ThrowableCollector.java:73)
        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.ThrowableCollector.execute(ThrowableCollector.java:73)
        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.ThrowableCollector.execute(ThrowableCollector.java:73)
        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 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

    还可以在表达式中执行一个方法,前提是方法的类在spring容器中注册了,@retryService其实就是获取bean name为retryService的bean,然后调用里面的checkException方法,传入的参数为#root,它其实就是抛出来的exception对象。一样的也是可以省略#{...}

        @Retryable(value = IllegalAccessException.class, exceptionExpression = "#{@retryService.checkException(#root)}")    public void service5(String exceptionMessage) throws IllegalAccessException {        log.info("do something... {}", LocalDateTime.now());        throw new IllegalAccessException(exceptionMessage);    }    @Retryable(value = IllegalAccessException.class, exceptionExpression = "@retryService.checkException(#root)")    public void service5_1(String exceptionMessage) throws IllegalAccessException {        log.info("do something... {}", LocalDateTime.now());        throw new IllegalAccessException(exceptionMessage);    }        public boolean checkException(Exception e) {        log.error("error message:{}", e.getMessage());        return true; //返回true的话表明会执行重试,如果返回false则不会执行重试    }

    运行结果:

    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

    当然还有更多表达式的用法了...

    @Retryable(exceptionExpression = "#{#root instanceof T(java.lang.IllegalAccessException)}") //判断exception的类型    public void service5_2(String exceptionMessage) {        log.info("do something... {}", LocalDateTime.now());        throw new NullPointerException(exceptionMessage);    }    @Retryable(exceptionExpression = "#root instanceof T(java.lang.IllegalAccessException)")    public void service5_3(String exceptionMessage) {        log.info("do something... {}", LocalDateTime.now());        throw new NullPointerException(exceptionMessage);    }
        @Retryable(exceptionExpression = "myMessage.contains('test')") //查看自定义的MyException中的myMessage的值是否包含test字符串    public void service5_4(String exceptionMessage) throws MyException {        log.info("do something... {}", LocalDateTime.now());        throw new MyException(exceptionMessage); //自定义的exception    }    @Retryable(exceptionExpression = "#root.myMessage.contains('test')")  //和上面service5_4方法的效果一样    public void service5_5(String exceptionMessage) throws MyException {        log.info("do something... {}", LocalDateTime.now());        throw new MyException(exceptionMessage);    }
    package org.example;import lombok.Getter;import lombok.Setter;@Getter@Setterpublic class MyException extends Exception {    private String myMessage;    public MyException(String myMessage) {        this.myMessage = myMessage;    }}

    下面再来看看另一个配置exclude

      @Retryable(exclude = MyException.class)    public void service6(String exceptionMessage) throws MyException {        log.info("do something... {}", LocalDateTime.now());        throw new MyException(exceptionMessage);    }

    这个exclude属性可以帮我们排除一些我们不想重试的异常

    最后我们来看看这个backoff 重试等待策略, 默认使用@Backoff注解。

    我们先来看看这个@Backoffvalue属性,用于设置重试间隔

     @Retryable(value = IllegalAccessException.class,            backoff = @Backoff(value = 2000))    public void service7() throws IllegalAccessException {        log.info("do something... {}", LocalDateTime.now());        throw new IllegalAccessException();    }

    运行结果可以看出来重试的间隔为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)
    ...

    接下来介绍@Backoffdelay属性,它与value属性不能共存,当delay不设置的时候会去读value属性设置的值,如果delay设置的话则会忽略value属性

    @Retryable(value = IllegalAccessException.class,            backoff = @Backoff(value = 2000,delay = 500))    public void service8() throws IllegalAccessException {        log.info("do something... {}", LocalDateTime.now());        throw new IllegalAccessException();    }

    运行结果可以看出,重试的时间间隔为500ms

    2021-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)

    接下来我们来看``@Backoffmultiplier`的属性, 指定延迟倍数, 默认为0。

    @Retryable(value = IllegalAccessException.class,maxAttempts = 4,            backoff = @Backoff(delay = 2000, multiplier = 2))    public void service9() throws IllegalAccessException {        log.info("do something... {}", LocalDateTime.now());        throw new IllegalAccessException();    }

    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)
    ...

    接下来我们来看看这个@BackoffmaxDelay属性,设置最大的重试间隔,当超过这个最大的重试间隔的时候,重试的间隔就等于maxDelay的值

    @Retryable(value = IllegalAccessException.class,maxAttempts = 4,            backoff = @Backoff(delay = 2000, multiplier = 2,maxDelay = 5000))    public void service10() throws IllegalAccessException {        log.info("do something... {}", LocalDateTime.now());        throw new IllegalAccessException();    }

    运行结果:

    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秒

    注解@Recover

    @Retryable方法重试失败之后,最后就会调用@Recover方法。用于@Retryable失败时的“兜底”处理方法。 @Recover的方法必须要与@Retryable注解的方法保持一致,第一入参为要重试的异常,其他参数与@Retryable保持一致,返回值也要一样,否则无法执行!

    @Retryable(value = IllegalAccessException.class)    public void service11() throws IllegalAccessException {        log.info("do something... {}", LocalDateTime.now());        throw new IllegalAccessException();    }    @Recover    public void recover11(IllegalAccessException e) {        log.info("service retry after Recover => {}", e.getMessage());    }    //=========================    @Retryable(value = ArithmeticException.class)    public int service12() throws IllegalAccessException {        log.info("do something... {}", LocalDateTime.now());        return 1 / 0;    }    @Recover    public int recover12(ArithmeticException e) {        log.info("service retry after Recover => {}", e.getMessage());        return 0;    }    //=========================    @Retryable(value = ArithmeticException.class)    public int service13(String message) throws IllegalAccessException {        log.info("do something... {},{}", message, LocalDateTime.now());        return 1 / 0;    }    @Recover    public int recover13(ArithmeticException e, String message) {        log.info("{},service retry after Recover => {}", message, e.getMessage());        return 0;    }

    注解@CircuitBreaker

    熔断模式:指在具体的重试机制下失败后打开断路器,过了一段时间,断路器进入半开状态,允许一个进入重试,若失败再次进入断路器,成功则关闭断路器,注解为@CircuitBreaker,具体包括熔断打开时间、重置过期时间

    // openTimeout时间范围内失败maxAttempts次数后,熔断打开resetTimeout时长 @CircuitBreaker(openTimeout = 1000, resetTimeout = 3000, value = NullPointerException.class)    public void circuitBreaker(int num) {        log.info(" 进入断路器方法num={}", num);        if (num > 8) return;        Integer n = null;        System.err.println(1 / n);    }    @Recover    public void recover(NullPointerException e) {        log.info("service retry after Recover => {}", e.getMessage());    }

    测试方法

    @Test    public void testCircuitBreaker() throws InterruptedException {        System.err.println("尝试进入断路器方法,并触发异常...");        retryService.circuitBreaker(1);        retryService.circuitBreaker(1);        retryService.circuitBreaker(9);        retryService.circuitBreaker(9);        System.err.println("在openTimeout 1秒之内重试次数为2次,未达到触发熔断, 断路器依然闭合...");        TimeUnit.SECONDS.sleep(1);        System.err.println("超过openTimeout 1秒之后, 因为未触发熔断,所以重试次数重置,可以正常访问...,继续重试3次方法...");        retryService.circuitBreaker(1);        retryService.circuitBreaker(1);        retryService.circuitBreaker(1);        System.err.println("在openTimeout 1秒之内重试次数为3次,达到触发熔断,不会执行重试,只会执行恢复方法...");        retryService.circuitBreaker(1);        TimeUnit.SECONDS.sleep(2);        retryService.circuitBreaker(9);        TimeUnit.SECONDS.sleep(3);        System.err.println("超过resetTimeout 3秒之后,断路器重新闭合...,可以正常访问");        retryService.circuitBreaker(9);        retryService.circuitBreaker(9);        retryService.circuitBreaker(9);        retryService.circuitBreaker(9);        retryService.circuitBreaker(9);    }

    运行结果:

    尝试进入断路器方法,并触发异常...
    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

    RetryTemplate

    RetryTemplate配置

    package org.example;import org.springframework.context.annotation.Bean;import org.springframework.context.annotation.Configuration;import org.springframework.retry.backoff.FixedBackOffPolicy;import org.springframework.retry.policy.SimpleRetryPolicy;import org.springframework.retry.support.RetryTemplate;@Configurationpublic class AppConfig {       @Bean    public RetryTemplate retryTemplate() {        RetryTemplate retryTemplate = new RetryTemplate();        SimpleRetryPolicy retryPolicy = new SimpleRetryPolicy(); //设置重试策略        retryPolicy.setMaxAttempts(2);        retryTemplate.setRetryPolicy(retryPolicy);        FixedBackOffPolicy fixedBackOffPolicy = new FixedBackOffPolicy(); //设置退避策略        fixedBackOffPolicy.setBackOffPeriod(2000L);        retryTemplate.setBackOffPolicy(fixedBackOffPolicy);        return retryTemplate;    }}

    可以看到这些配置跟我们直接写注解的方式是差不多的,这里就不过多的介绍了。。

    使用RetryTemplate

    package org.example;import org.junit.jupiter.api.Test;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.boot.test.context.SpringBootTest;import org.springframework.retry.RetryCallback;import org.springframework.retry.RetryContext;import org.springframework.retry.support.RetryTemplate;@SpringBootTestpublic class RetryTemplateTest {    @Autowired    private RetryTemplate retryTemplate;    @Autowired    private RetryTemplateService retryTemplateService;    @Test    void test1() throws IllegalAccessException {        retryTemplate.execute(new RetryCallback<Object, IllegalAccessException>() {            @Override            public Object doWithRetry(RetryContext context) throws IllegalAccessException {                 retryTemplateService.service1();                return null;            }        });    }        @Test    void test2() throws IllegalAccessException {        retryTemplate.execute(new RetryCallback<Object, IllegalAccessException>() {            @Override            public Object doWithRetry(RetryContext context) throws IllegalAccessException {                retryTemplateService.service1();                return null;            }        }, new RecoveryCallback<Object>() {            @Override            public Object recover(RetryContext context) throws Exception {                log.info("RecoveryCallback....");                return null;            }        });    }}

    RetryOperations定义重试的API,RetryTemplate是API的模板模式实现,实现了重试和熔断。提供的API如下:

    package org.springframework.retry;import org.springframework.retry.support.DefaultRetryState;public interface RetryOperations {<T, E extends Throwable> T execute(RetryCallback<T, E> retryCallback) throws E;<T, E extends Throwable> T execute(RetryCallback<T, E> retryCallback, RecoveryCallback<T> recoveryCallback)throws E;<T, E extends Throwable> T execute(RetryCallback<T, E> retryCallback, RetryState retryState)throws E, ExhaustedRetryException;<T, E extends Throwable> T execute(RetryCallback<T, E> retryCallback, RecoveryCallback<T> recoveryCallback,RetryState retryState) throws E;}

    下面主要介绍一下RetryTemplate配置的时候,需要设置的重试策略和退避策略

    RetryPolicy

    RetryPolicy是一个接口, 然后有很多具体的实现,我们先来看看它的接口中定义了什么方法

    package org.springframework.retry;import java.io.Serializable;public interface RetryPolicy extends Serializable {boolean canRetry(RetryContext context); RetryContext open(RetryContext parent);void close(RetryContext context);void reGISterThrowable(RetryContext context, Throwable throwable);}

    我们来看看他有什么具体的实现类

    • 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可以监听重试,并执行对应的回调方法

    package org.springframework.retry;public interface RetryListener {<T, E extends Throwable> boolean open(RetryContext context, RetryCallback<T, E> callback);<T, E extends Throwable> void close(RetryContext context, RetryCallback<T, E> callback, Throwable throwable);<T, E extends Throwable> void onError(RetryContext context, RetryCallback<T, E> callback, Throwable throwable);}

    使用如下:

    自定义一个Listener

    package org.example;import lombok.extern.slf4j.Slf4j;import org.springframework.retry.RetryCallback;import org.springframework.retry.RetryContext;import org.springframework.retry.listener.RetryListenerSupport;@Slf4jpublic class DefaultListenerSupport extends RetryListenerSupport {    @Override    public <T, E extends Throwable> void close(RetryContext context,                                               RetryCallback<T, E> callback, Throwable throwable) {        log.info("onClose");        super.close(context, callback, throwable);    }    @Override    public <T, E extends Throwable> void onError(RetryContext context,                                                 RetryCallback<T, E> callback, Throwable throwable) {        log.info("onError");        super.onError(context, callback, throwable);    }    @Override    public <T, E extends Throwable> boolean open(RetryContext context,                                                 RetryCallback<T, E> callback) {        log.info("onOpen");        return super.open(context, callback);    }}

    把listener设置到retryTemplate中

    package org.example;import lombok.extern.slf4j.Slf4j;import org.springframework.context.annotation.Bean;import org.springframework.context.annotation.Configuration;import org.springframework.retry.backoff.FixedBackOffPolicy;import org.springframework.retry.policy.SimpleRetryPolicy;import org.springframework.retry.support.RetryTemplate;@Configuration@Slf4jpublic class AppConfig {    @Bean    public RetryTemplate retryTemplate() {        RetryTemplate retryTemplate = new RetryTemplate();        SimpleRetryPolicy retryPolicy = new SimpleRetryPolicy(); //设置重试策略        retryPolicy.setMaxAttempts(2);        retryTemplate.setRetryPolicy(retryPolicy);        FixedBackOffPolicy fixedBackOffPolicy = new FixedBackOffPolicy(); //设置退避策略        fixedBackOffPolicy.setBackOffPeriod(2000L);        retryTemplate.setBackOffPolicy(fixedBackOffPolicy);        retryTemplate.registerListener(new DefaultListenerSupport()); //设置retryListener        return 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

    到此,关于“Spring Boot中怎么使用Spring-Retry重试框架”的学习就结束了,希望能够解决大家的疑惑。理论与实践的搭配能更好的帮助大家学习,快去试试吧!若想继续学习更多相关知识,请继续关注编程网网站,小编会继续努力为大家带来更多实用的文章!

    --结束END--

    本文标题: Spring Boot中怎么使用Spring-Retry重试框架

    本文链接: https://www.lsjlt.com/news/328003.html(转载时请注明来源链接)

    有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

    本篇文章演示代码以及资料文档资料下载

    下载Word文档到电脑,方便收藏和打印~

    下载Word文档
    猜你喜欢
    • Spring Boot中怎么使用Spring Retry重试框架
      今天小编给大家分享一下Spring Boot中怎么使用Spring Retry重试框架的相关知识点,内容详细,逻辑清晰,相信大部分人都还太了解这方面的知识,所以分享这篇文章给大家参考一下,希望大家阅读完这篇文章后有所收获...
      99+
      2023-06-30
    • Spring Boot中怎么使用Spring-Retry重试框架
      这篇文章主要介绍“Spring Boot中怎么使用Spring-Retry重试框架”,在日常操作中,相信很多人在Spring Boot中怎么使用Spring-Retry重试框架问题上存在疑惑,小编查阅了各式资料,整理出简...
      99+
      2023-06-30
    • Spring Boot中使用Spring Retry重试框架的操作方法
      目录Spring Retry 在SpringBoot 中的应用Maven依赖注解使用开启Retry功能注解@Retryable注解@Recover注解@CircuitBreakerR...
      99+
      2022-11-13
    • Spring Retry重试框架如何使用
      这篇“Spring Retry重试框架如何使用”文章的知识点大部分人都不太理解,所以小编给大家总结了以下内容,内容详细,步骤清晰,具有一定的借鉴价值,希望大家阅读完这篇文章能有所收获,下面我们一起来看看这篇“Spring ...
      99+
      2023-07-04
    • Spring Retry重试框架的使用讲解
      目录命令式声明式(注解方式)1. 用法2. RetryTemplate3. RecoveryCallback4. Listeners5. 声明式重试重试的使用场景比较多,比如调用远程...
      99+
      2022-11-13
      Spring Retry重试 Spring Retry
    • SpringBoot中使用Spring-Retry重试框架的实现
      目录Maven依赖注解使用开启Retry功能注解@Retryable注解@Recover注解@CircuitBreakerRetryTemplateRetryTemplate配置使用...
      99+
      2022-11-13
    • Spring Retry重试怎么使用
      本篇内容介绍了“Spring Retry重试怎么使用”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!spring-retry是什么?...
      99+
      2023-07-04
    • 【SpringBoot】75、SpringBoot中使用spring-retry轻松解决重试
      在日常开发过程中,难免会与第三方接口发生交互,例如:短信发送、远程服务调用、争抢锁等场景,当正常调用发生异常时,例如:网络抖动,这些间歇性的异常在一段时候之后会自行恢复,程序为了更加健壮并且更不容易出...
      99+
      2023-09-29
      spring boot java retry 重试 aop
    • 怎么用spring retry方法调用失败重试机制
      这篇文章主要介绍“怎么用spring retry方法调用失败重试机制”的相关知识,小编通过实际案例向大家展示操作过程,操作方法简单快捷,实用性强,希望这篇“怎么用spring retry方法调用失败重试机制”文章能帮助大...
      99+
      2023-06-29
    • Spring Boot中怎么使用Spring MVC
      本篇内容主要讲解“Spring Boot中怎么使用Spring MVC”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“Spring Boot中怎么使用Spring&n...
      99+
      2023-07-06
    • Spring框架中一个有用的小组件之Spring Retry组件详解
      1、概述 Spring Retry 是Spring框架中的一个组件, 它提供了自动重新调用失败操作的能力。这在错误可能是暂时发生的(如瞬时网络故障)的情况下很有帮助。 在本文中,我们...
      99+
      2022-11-12
    • 使用Spring-Retry解决SpringBoot应用程序中的重试问题
      目录1、背景2、引入依赖3、开启spring-retry4、在方法上添加@Retryable5、重试完6、注意事项1、背景 在日常开发过程中,难免会与第三方接口发生交互,例如:远程服...
      99+
      2023-05-16
      Spring-Retry解决重试 SpringBoot重试问题
    • spring retry实现方法请求重试的使用步骤
      目录1 spring-retry是什么?2 使用步骤2.1 引入maven库2.2 在spring启动类上开启重试功能2.3 公共业务代码2.4 传统的重试做法2.5 使用sprin...
      99+
      2022-11-13
    • 探索Java中最常用的框架:Spring、Spring MVC、Spring Boot、MyBatis和Netty
      文章目录 Spring框架Spring MVC框架Spring Boot框架MyBatis框架Netty框架总结 🎉欢迎来到Java面试技巧专栏~探索Ja...
      99+
      2023-10-26
      java spring mvc 原力计划
    • 怎么进行Spring-Retry的使用
      怎么进行Spring-Retry的使用,针对这个问题,这篇文章详细介绍了相对应的分析和解答,希望可以帮助更多想解决这个问题的小伙伴找到更简单易行的方法。1 Spring-Retry的简介在日常的一些场景中, 很多需要进行重试的操作.而spr...
      99+
      2023-06-25
    • spring框架怎么使用redis
      "spring框架使用redis的方法:在pom.xml中导入redis的相关依赖,例如:<dependency><groupId>redis.clients</groupId><arti...
      99+
      2022-10-11
    • 怎么在java中使用Spring框架
      怎么在java中使用Spring框架?相信很多没有经验的人对此束手无策,为此本文总结了问题出现的原因和解决方法,通过这篇文章希望你能解决这个问题。Java是什么Java是一门面向对象编程语言,可以编写桌面应用程序、Web应用程序、分布式系统...
      99+
      2023-06-14
    • Spring Boot验证码框架CAPTCHA有什么用
      这篇文章主要介绍Spring Boot验证码框架CAPTCHA有什么用,文中介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们一定要看完!CAPTCHA简介人机识别与验证码是后端开发中保护接口最常见的方式之一,而此类代码往往会与业务代码耦...
      99+
      2023-06-14
    • 怎么使用Spring Boot
      本篇内容介绍了“怎么使用Spring Boot”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!1. 关闭指定的自动配置通过上面的 @Enabl...
      99+
      2023-06-02
    • 一文教你正确的使用Spring boot框架
      今天就跟大家聊聊有关一文教你正确的使用Spring boot框架,可能很多人都不太了解,为了让大家更加了解,小编给大家总结了以下内容,希望大家根据这篇文章可以有所收获。前言首先来说一下为什么使用 Spring Boot,之前我用的后端 WE...
      99+
      2023-05-31
      springboot spring boo
    软考高级职称资格查询
    编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
    • 官方手机版

    • 微信公众号

    • 商务合作