SpringBoot 技术实践 -SpringRetry 重试框架
// 重试策略
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;
}
}
在 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;
}
}
访问 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 开启重试,在类上指定的时候方法将默认执行,重试三次
定义 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("手工异常");
}
}
controller 中注入 service
@RequestMapping("/retryAnnotation")
public Object retryAnnotation() {
retryService.retryServiceCall();
return "retryAnnotation";
}
将会默认重试
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 注解参数
value:指定发生的异常进行重试
include:和 value 一样,默认空,当 exclude 也为空时,所有异常都重试
exclude:指定异常不重试,默认空,当 include 也为空时,所有异常都重试
maxAttemps:重试次数,默认 3
backoff:重试补偿机制,默认没有
@Backoff 注解 重试补偿策略
不设置参数时,默认使用 FixedBackOffPolicy(指定等待时间),重试等待 1000ms
设置 delay,使用 FixedBackOffPolicy(指定等待设置 delay 和 maxDealy 时,重试等待在这两个值之间均态分布)
设置 delay、maxDealy、multiplier,使用 ExponentialBackOffPolicy(指数级重试间隔的实现),multiplier 即指定延迟倍数,比如 delay=5000L,multiplier=2,则第一次重试为 5 秒,第二次为 10 秒,第三次为 20 秒
@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 de
lay 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()} > 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;
}
@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))
监听方法,在配置类中进行配置
/**
注解调用
*/
@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);
}
};
}
调用服务
@RestController
public class SpringRetryController {
@Resource
private PlatformClassService platformClassService;
@RequestMapping("/retryPlatformCall")
评论