Spring Retry 在SpringBoot 中的应用

本文详细介绍了如何在Spring Boot中使用Spring Retry重试框架。内容涵盖Maven依赖、注解使用、RetryTemplate配置,包括RetryPolicy、BackOffPolicy和RetryListener的设置。通过实例展示了如何设置重试次数、重试间隔、异常处理和退避策略,帮助开发者理解并应用Spring Retry进行错误处理。
摘要由CSDN通过智能技术生成

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
@EnableRetry
public 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
@Slf4j
public 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)
@Documented
public @interface Retryable {

	/**
	 * Retry interceptor bean name to be applied for retryable method. Is mutually
	 * exclusive with other attributes.
	 * @return the retry interceptor bean name
	 */
	String interceptor() default "";

	/**
	 * Exception types that are retryable. Synonym for includes(). Defaults to empty (and
	 * if excludes is also empty all exceptions are retried).
	 * @return exception types to retry
	 */
	Class<? extends Throwable>[] value() default {};

	/**
	 * Exception types that are retryable. Defaults to empty (and if excludes is also
	 * empty all exceptions are retried).
	 * @return exception types to retry
	 */
	Class<? extends Throwable>[] include() default {};

	/**
	 * Exception types that are not retryable. Defaults to empty (and if includes is also
	 * empty all exceptions are retried).
	 * If includes is empty but excludes is not, all not excluded exceptions are retried
	 * @return exception types not to retry
	 */
	Class<? extends Throwable>[] exclude() default {};

	/**
	 * A unique label for statistics reporting. If not provided the caller may choose to
	 * ignore it, or provide a default.
	 *
	 * @return the label for the statistics
	 */
	String label() default "";

	/**
	 * Flag to say that the retry is stateful: i.e. exceptions are re-thrown, but the
	 * retry policy is applied with the same policy to subsequent invocations with the
	 * same arguments. If false then retryable exceptions are not re-thrown.
	 * @return true if retry is stateful, default false
	 */
	boolean stateful() default false;

	/**
	 * @return the maximum number of attempts (including the first failure), defaults to 3
	 */
	int maxAttempts() default 3;  //默认重试次数3次

	/**
	 * @return an expression evaluated to the maximum number of attempts (including the first failure), defaults to 3
	 * Overrides {@link #maxAttempts()}.
	 * @since 1.2
	 */
	String maxAttemptsExpression() default "";

	/**
	 * Specify the backoff properties for retrying this operation. The default is a
	 * simple {@link Backoff} specification with no properties - see it's documentation
	 * for defaults.
	 * @return a backoff specification
	 */
	Backoff backoff() default @Backoff(); //默认的重试中的退避策略

	/**
	 * Specify an expression to be evaluated after the {@code SimpleRetryPolicy.canRetry()}
	 * returns true - can be used to conditionally suppress the retry. Only invoked after
	 * an exception is thrown. The root object for the evaluation is the last {@code Throwable}.
	 * Other beans in the context can be referenced.
	 * For example:
	 * <pre class=code>
	 *  {@code "message.contains('you can retry this')"}.
	 * </pre>
	 * and
	 * <pre class=code>
	 *  {@code "@someBean.shouldRetry(#root)"}.
	 * </pre>
	 * @return the expression.
	 * @since 1.2
	 */
	String exceptionExpression() default "";

	/**
	 * Bean names of retry listeners to use instead of default ones defined in Spring context
	 * @return retry listeners bean names
	 */
	String[] listeners() default {};

}
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Backoff {

	/**
	 * Synonym for {@link #delay()}.
	 *
	 * @return the delay in milliseconds (default 1000)
	 */
	long value() default 1000; //默认的重试间隔1秒

	/**
	 * A canonical backoff period. Used as an initial value in the exponential case, and
	 * as a minimum value in the uniform case.
	 * @return the initial or canonical backoff period in milliseconds (default 1000)
	 */
	long delay() default 0;

	/**
	 * The maximimum wait (in milliseconds) between retries. If less than the
	 * {@link #delay()} then the default of
	 * {@value org.springframework.retry.backoff.ExponentialBackOffPolicy#DEFAULT_MAX_INTERVAL}
	 * is applied.
	 *
	 * @return the maximum delay between retries (default 0 = ignored)
	 */
	long maxDelay() default 0;

	/**
	 * If positive, then used as a multiplier for generating the next delay for backoff.
	 *
	 * @return a multiplier to use to calculate the next backoff delay (default 0 =
	 * ignored)
	 */
	double multiplier() default 0;

	/**
	 * An expression evaluating to the canonical backoff period. Used as an initial value
	 * in the exponential case, and as a minimum value in the uniform case. Overrides
	 * {@link #delay()}.
	 * @return the initial or canonical backoff period in milliseconds.
	 * @since 1.2
	 */
	String delayExpression() default "";

	/**
	 * An expression evaluating to the maximimum wait (in milliseconds) between retries.
	 * If less than the {@link #delay()} then the default of
	 * {@value org.springframework.retry.backoff.ExponentialBackOffPolicy#DEFAULT_MAX_INTERVAL}
	 * is applied. Overrides {@link #maxDelay()}
	 *
	 * @return the maximum delay between retries (default 0 = ignored)
	 * @since 1.2
	 */
	String maxDelayExpression() default "";

	/**
	 * Evaluates to a vaule used as a multiplier for generating the next delay for
	 * backoff. Overrides {@link #multiplier()}.
	 *
	 * @return a multiplier expression to use to calculate the next backoff delay (default
	 * 0 = ignored)
	 * @since 1.2
	 */
	String multiplierExpression() default "";

	/**
	 * In the exponential case ({@link #multiplier()} > 0) set this to true to have the
	 * backoff delays randomized, so that the maximum delay is multiplier times the
	 * previous delay and the distribution is uniform between the two values.
	 *
	 * @return the flag to signal randomization is required (default false)
	 */
	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;

@SpringBootTest
class 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.invokeTestMetho
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值