SpringBoot技术实践-SpringRetry重试框架

愿天堂没有BUG

共 14862字,需浏览 30分钟

 ·

2021-07-31 08:45

一、环境搭建

  1. 加入SpringRetry依赖,SpringRetry使用AOP实现,所以也需要加入AOP包

<!-- SpringRetry -->
<dependency>
<groupId>org.springframework.retry</groupId>
<artifactId>spring-retry</artifactId>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
</dependency>
复制代码
  1. 官方文档

    • www.baeldung.com/spring-retr…

二、RetryTemplate

2.1 RetryTemplate

  1. RetryTemplate封装了Retry基本操作

    • org.springframework.retry.support.RetryTemplate

  2. RetryTemplate中可以指定监听、回退策略、重试策略等

  3. 只需要正常new RetryTemplate()即可使用

2.2 RetryListener

  1. RetryListener指定了当执行过程中出现错误时的回调

    • org.springframework.retry.RetryListener

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);
}
复制代码
  1. 配置之后在RetryTemplate中指定

2.3 回退策略

2.3.1 FixedBackOffPolicy

  1. 当出现错误时延迟多少时间继续调用

FixedBackOffPolicy fixedBackOffPolicy = new FixedBackOffPolicy();
fixedBackOffPolicy.setBackOffPeriod(1000L);
retryTemplate.setBackOffPolicy(fixedBackOffPolicy);
复制代码
  1. 配置之后在RetryTemplate中指定

2.3.2 ExponentialBackOffPolicy

  1. 当出现错误时第一次按照指定延迟时间延迟后按照指数进行延迟

// 指数回退(秒),第一次回退1s,第二次回退2s,第三次4秒,第四次8秒
ExponentialBackOffPolicy exponentialBackOffPolicy = new ExponentialBackOffPolicy();
exponentialBackOffPolicy.setInitialInterval(1000L);
exponentialBackOffPolicy.setMultiplier(2);
retryTemplate.setBackOffPolicy(exponentialBackOffPolicy);
复制代码
  1. 配置之后在RetryTemplate中指定

2.4 重试策略

  1. 重试策略主要指定出现错误时重试次数

// 重试策略
SimpleRetryPolicy retryPolicy = new SimpleRetryPolicy();
retryPolicy.setMaxAttempts(5);
retryTemplate.setRetryPolicy(retryPolicy);
复制代码
  1. 配置之后在RetryTemplate中指定

2.5 RetryCallback

  1. RetryCallback为retryTemplate.execute时执行的回调

    • public final <T, E extends Throwable> T execute(RetryCallback<T, E> retryCallback) throws E

2.6 核心使用

  1. 可以使用RetryTemplate完成简单使用

  2. 配置retryTemplate

    • 指定回退策略为ExponentialBackOffPolicy

    • 指定重试策略为SimpleRetryPolicy

    • 指定监听器RetryListener

import com.codecoord.util.PrintUtil;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.retry.RetryCallback;
import org.springframework.retry.RetryContext;
import org.springframework.retry.RetryListener;
import org.springframework.retry.backoff.ExponentialBackOffPolicy;
import org.springframework.retry.policy.SimpleRetryPolicy;
import org.springframework.retry.support.RetryTemplate;

@Configuration
public class RetryTemplateConfig {

/**
* 注入retryTemplate
*/

@Bean
public RetryTemplate retryTemplate() {
RetryTemplate retryTemplate = new RetryTemplate();

/// 回退固定时间(秒)
/* FixedBackOffPolicy fixedBackOffPolicy = new FixedBackOffPolicy();
fixedBackOffPolicy.setBackOffPeriod(1000L);
retryTemplate.setBackOffPolicy(fixedBackOffPolicy);*/


// 指数回退(秒),第一次回退1s,第二次回退2s
ExponentialBackOffPolicy exponentialBackOffPolicy = new ExponentialBackOffPolicy();
exponentialBackOffPolicy.setInitialInterval(1000L);
exponentialBackOffPolicy.setMultiplier(2);
retryTemplate.setBackOffPolicy(exponentialBackOffPolicy);

// 重试策略
SimpleRetryPolicy retryPolicy = new SimpleRetryPolicy();
retryPolicy.setMaxAttempts(5);
retryTemplate.setRetryPolicy(retryPolicy);

// 设置监听器,open和close分别在启动和结束时执行一次
RetryListener[] listeners = {
new RetryListener() {
@Override
public <T, E extends Throwable> boolean open(RetryContext context, RetryCallback<T, E> callback) {
PrintUtil.print("open");
return true;
}
@Override
public <T, E extends Throwable> void close(RetryContext context, RetryCallback<T, E> callback,
Throwable throwable)
{
PrintUtil.print("close");
}
@Override
public <T, E extends Throwable> void onError(RetryContext context, RetryCallback<T, E> callback,
Throwable throwable)
{
PrintUtil.print("onError");
}
}
};
retryTemplate.setListeners(listeners);

return retryTemplate;
}
}

复制代码
  1. 在controller中注入RetryTemplate使用,也可以是在service中

@RestController
public class SpringRetryController {
@Resource
private RetryTemplate retryTemplate;
private static int count = 0;

@RequestMapping("/retry")
public Object retry() {
try {
count = 0;
retryTemplate.execute((RetryCallback<Void, RuntimeException>) context -> {
// 业务代码
// ....
// 模拟抛出异常
++count;
throw new RuntimeException("抛出异常");
});
} catch (RuntimeException e) {
System.out.println("Exception");
}

return "retry = " + count;
}
}
复制代码
  1. 访问retry接口,然后观察日志输出

18:27:20.648 - http-nio-8888-exec-1 - open
18:27:20.649 - http-nio-8888-exec-1 - retryTemplate.execute执行
18:27:20.649 - http-nio-8888-exec-1 - onError
18:27:21.658 - http-nio-8888-exec-1 - retryTemplate.execute执行
18:27:21.658 - http-nio-8888-exec-1 - onError
18:27:23.670 - http-nio-8888-exec-1 - retryTemplate.execute执行
18:27:23.670 - http-nio-8888-exec-1 - onError
18:27:27.679 - http-nio-8888-exec-1 - retryTemplate.execute执行
18:27:27.679 - http-nio-8888-exec-1 - onError
18:27:35.681 - http-nio-8888-exec-1 - retryTemplate.execute执行
18:27:35.681 - http-nio-8888-exec-1 - onError
18:27:35.681 - http-nio-8888-exec-1 - close
复制代码

三、EnableRetry

  1. @EnableRetry开启重试,在类上指定的时候方法将默认执行,重试三次

  2. 定义service,开启@EnableRetry注解和指定@Retryable,重试可以参考后面一节

import org.springframework.retry.annotation.Retryable;

public interface RetryService {

/**
* 重试方法调用
*/

@Retryable
void retryServiceCall();
}

复制代码
import org.springframework.retry.annotation.EnableRetry;
import org.springframework.stereotype.Service;

@EnableRetry
@Service
public class RetryServiceImpl implements RetryService {

@Override
public void retryServiceCall() {
PrintUtil.print("方法调用..");
throw new RuntimeException("手工异常");
}
}

复制代码
  1. controller中注入service

@RequestMapping("/retryAnnotation")
public Object retryAnnotation() {
retryService.retryServiceCall();
return "retryAnnotation";
}
复制代码
  1. 将会默认重试

18:46:48.721 - http-nio-8888-exec-1 - 方法调用..
18:46:49.724 - http-nio-8888-exec-1 - 方法调用..
18:46:50.730 - http-nio-8888-exec-1 - 方法调用..
java.lang.RuntimeException: 手工异常
复制代码

四、Retryable

  1. 用于需要重试的方法上的注解

  2. 有以下几个属性

    • 不设置参数时,默认使用FixedBackOffPolicy(指定等待时间),重试等待1000ms

    • 设置delay,使用FixedBackOffPolicy(指定等待设置delay和maxDealy时,重试等待在这两个值之间均态分布)

    • 设置delay、maxDealy、multiplier,使用 ExponentialBackOffPolicy(指数级重试间隔的实现),multiplier即指定延迟倍数,比如delay=5000L,multiplier=2,则第一次重试为5秒,第二次为10秒,第三次为20秒

    • value:指定发生的异常进行重试

    • include:和value一样,默认空,当exclude也为空时,所有异常都重试

    • exclude:指定异常不重试,默认空,当include也为空时,所有异常都重试

    • maxAttemps:重试次数,默认3

    • backoff:重试补偿机制,默认没有

    • Retryable注解参数

    • @Backoff  注解 重试补偿策略

@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;

/**
* @return an expression evaluated to the maximum number of attempts (including the first failure), defaults to 3
* Overrides {@link #maxAttempts()}.
* @date 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.
* @date 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;

/**
* 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.
* @date 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)
* @date 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)
* @date 1.2
*/

String multiplierExpression() default "";

/**
* In the exponential case ({@link #multiplier()} &gt; 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;

}
复制代码
  1. 在需要重试的方法上配置对应的重试次数、重试异常的异常类型、设置回退延迟时间、重试策略、方法监听名称

@Component
public class PlatformClassService {
@Retryable(
// 重试异常的异常类型
value = {Exception.class},
// 最大重试次数
maxAttempts = 5,
// 设置回退延迟时间
backoff = @Backoff(delay = 500),
// 配置回调方法名称
listeners = "retryListener"
)

public void call() {
System.out.println("call...");
throw new RuntimeException("手工异常");
}
}
复制代码
// 初始延迟2秒,然后之后验收1.5倍延迟重试,总重试次数4
@Retryable(value = {Exception.class}, maxAttempts = 4, backoff = @Backoff(delay = 2000L, multiplier = 1.5))
复制代码
  1. 监听方法,在配置类中进行配置

/**
* 注解调用
*/

@Bean
public RetryListener retryListener() {
return new RetryListener() {
@Override
public <T, E extends Throwable> boolean open(RetryContext context, RetryCallback<T, E> callback) {
System.out.println("open context = " + context + ", callback = " + callback);
// 返回true继续执行后续调用
return true;
}

@Override
public <T, E extends Throwable> void close(RetryContext context, RetryCallback<T, E> callback,
Throwable throwable)
{
System.out.println("close context = " + context + ", callback = " + callback);
}
@Override
public <T, E extends Throwable> void onError(RetryContext context, RetryCallback<T, E> callback,
Throwable throwable)
{
System.out.println("onError context = " + context + ", callback = " + callback);
}
};
}
复制代码
  1. 调用服务

@RestController
public class SpringRetryController {
@Resource
private PlatformClassService platformClassService;

@RequestMapping("/retryPlatformCall")
public Object retryPlatformCall() {
try {
platformClassService.call();
} catch (Exception e) {
return "尝试调用失败";
}
return "retryPlatformCall";
}
}
复制代码
  1. 调用结果


作者:TianXinCoord
链接:https://juejin.cn/post/6988384668500361229
来源:掘金
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。



浏览 96
点赞
评论
收藏
分享

手机扫一扫分享

举报
评论
图片
表情
推荐
点赞
评论
收藏
分享

手机扫一扫分享

举报