1、介绍
日常开发中经常需要调用第三方接口,有些接口需要在特定异常下进行重试,为了避免一直在调用接口,每次调用直接需要间隔一段时间,并且需要设置个上限,达到最大重试次数后抛出异常;对该异常进行一致性处理,按一致性补偿处理或者记录异常并推送提醒。
常用的做法是写个循环,不断调用接口,并设置睡眠时间;手动写重试方法需要考虑的异常问题较多,这里介绍个spring自带的retry,使用简单,即插即用。@Retryable也是通过AOP方式实现,因此重试的方法不能在同一类中调用。
2、 Retryable使用
在启动类中添加@EnableRetry注解
@EnableRetry
@ServletComponentScan
@SpringBootApplication
public class WebApplication extends SpringBootServletInitializer {
public static void main(String[] args) {
SpringApplication.run(WebApplication.class, args);
}
/**
* 支持打成war包部署
*
* @date
*/
@Override
protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
return application.sources(WebApplication.class);
}
}
业务service类中添加重试方法,通过@Retryable注解设置重试策略
int count = 0;
@Retryable(include = {RuntimeException.class}, maxAttempts = 3, backoff = @Backoff(delay = 2000L, multiplier = 2))
public void retryMethod() {
System.out.println("执行重试count=" + count++ + " 执行时间" + LocalDateTime.now());
/**
* 调用第三方接口 异常
*/
//抛出异常
throw new RuntimeException();
}
执行结果
执行重试count=0 执行时间2021-01-05T17:18:18.124
执行重试count=1 执行时间2021-01-05T17:18:20.124
执行重试count=2 执行时间2021-01-05T17:18:24.126
常用的参数
- value/include 处理的特定异常
- maxAttempts 最大重试次数,默认3次
- backoff 指定的重试策略,delay为第一次延迟时间(毫秒),multiplier为倍数
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;
/**
* @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 {};
}
若要在达到最大次数后,处理特定异常,进行一致性任务处理,事务回滚或者推送提醒等,可通过@Recover处理,达到执行最大重试次数,会执行该方法。其中recover方法中的入参为重试中抛出的指定异常,且重试方法返回参数必须为void。
@Recover
public void recover(RuntimeException e) {
log.error("recover method", e);
System.out.println("一致性处理补偿 : ");
}
3、 自定义策略
若需要控制第三方接口的超时时间,以及需要返回重试接口的错误异常信息,可以通过自定义策略模块,重写dowithRetry方法实现,自定义重试策略的超时时间。
public void retryPolicy() {
RetryTemplate template = new RetryTemplate();
TimeoutRetryPolicy policy = new TimeoutRetryPolicy();
policy.setTimeout(6000L);
template.setRetryPolicy(policy);
String result = template.execute(new RetryCallback<String, RuntimeException>() {
@Override
public String doWithRetry(RetryContext context) {
// Do stuff that might fail, e.g. webservice operation
return "";
}
});
}
其中doWithRetry方法的返回参数可按需要返回所要的类型,TimeoutRetryPolicy为超时的策略继承了RetryPolicy接口。
4、总结
spring自带的重试机制,使用方便,简单,可指定重试异常和重试间隔时间等;并且提供自定义重试模板方法,自定义重试策略,可对接口调用超时异常进行处理重试。spring自带的重试方法在spring家族使用广泛,spring batch和spring单例等方法中都有使用。