什么是重試
重試是指,當(dāng)在一個(gè)程序運(yùn)行過(guò)程中,突然遇到了例如網(wǎng)絡(luò)延遲,中斷等情況時(shí),為了保證程序容錯(cuò)性,可用性,一致性等的一個(gè)措施,目前主流的框架大多都有一套自己的重試機(jī)制,例如 dubbo,mq,Spring 等
概要
Spring 也自己實(shí)現(xiàn)了一套重試機(jī)制,Spring Retry 是從 Spring batch 中獨(dú)立出來(lái)的一個(gè)功能,主要功能點(diǎn)在于重試和熔斷,目前已經(jīng)廣泛應(yīng)用于 Spring Batch,Spring Integration, Spring for Apache Hadoop 等 Spring 項(xiàng)目。spring retry 提供了注解和編程 兩種支持,提供了 RetryTemplate 支持,類似 RestTemplate。整個(gè)流程如下:
使用介紹
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 功能,需在啟動(dòng)類中使用 @EnableRetry 注解
@SpringBootApplication
@EnableRetry
@EnableScheduling
public class DemoApplication {
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
}
注解 @Retryable
需要在重試的代碼中加入重試注解 @Retryable
@Retryable(value = RuntimeException.class)
public void testRetry01() throws MyException {
System.out.println("測(cè)試-value屬性");
throw new RuntimeException("出現(xiàn)了異常");
}
默認(rèn)情況下,會(huì)重試 3 次,間隔 1 秒
重試配置
通過(guò) @Retryable 注解的屬性 可以實(shí)現(xiàn)重試配置
- Value()
- includevalue 與 include 含義相同,表示可重試的異常類型。默認(rèn)為空,如果同時(shí) exclude 也為空則會(huì)重試所有異常。但在使用時(shí)需要注意
@Retryable(value = RuntimeException.class)
public void testRetry01() throws MyException {
System.out.println("測(cè)試-value屬性");
throw new RuntimeException("出現(xiàn)了異常");
}
例:testRetry01 只會(huì)在程序拋出 RuntimeException 時(shí),開啟重試
- exclude不可重試的異常類型。默認(rèn)為空(如果 include 也為為空,將重試所有異常)。如果 include 為空但 exclude 不為空,則重試非 exclude 中的異常 @Retryable(exclude = RuntimeException.class)
public void testRetry02() throws MyException {
System.out.println("測(cè)試-value屬性");
throw new MyException("出現(xiàn)了異常");
}例:testRetry02 在程序拋出 MyException 時(shí),不會(huì)開啟重試 - maxAttempts
最大重試次數(shù),默認(rèn)為 3
- maxAttemptsExpression
最大嘗試次數(shù)的表達(dá)式,表達(dá)式一旦設(shè)置了值,則會(huì)覆蓋 maxAttempts 的值,maxAttemptsExpression 可以讀取 application.yml 配置文件里的數(shù)據(jù),也可以通過(guò) SpEL 表達(dá)式計(jì)算對(duì)應(yīng)的值
@Retryable(value = MyException.class, maxAttemptsExpression = "${maxAttempts}")
public void testRetry03() throws MyException {
System.out.println("測(cè)試-maxAttemptsExpression屬性");
throw new MyException("出現(xiàn)了異常");
}
例:testRetry03 會(huì)去讀 properties 配置文件獲取屬性名為 maxAttempts 的值
@Retryable(value = MyException.class, maxAttemptsExpression = "#{2+3}")
public void testRetry04() throws MyException {
System.out.println("測(cè)試-maxAttemptsExpression屬性");
throw new MyException("出現(xiàn)了異常");
}
例:testRetry04 會(huì)去通過(guò) SqlEL 計(jì)算出對(duì)應(yīng)的重試值
- exceptionExpression
異常處理表達(dá)式,ExpressionRetryPolicy 中使用,執(zhí)行完父類的 canRetry 之后,需要校驗(yàn) exceptionExpression 的值,為 true 則可以重試
@Retryable(value = MyException.class, exceptionExpression = "#{@retryService.isRetry()}")
public void testRetry05() throws MyException {
System.out.println("測(cè)試-exceptionExpression");
throw new MyException("出現(xiàn)了異常");
}
例:這個(gè)表達(dá)式的意思就是,如果 testRetry05 方法出現(xiàn)異常 會(huì)調(diào)用 retryService.isRetry() 方法,根據(jù)返回結(jié)果判斷是否重試
- @Recover兜底方法
當(dāng) @Retryable 方法重試失敗之后,最后就會(huì)調(diào)用 @Recover 方法。用于 @Retryable 失敗時(shí)的“兜底”處理方法。 @Recover 的方法必須要與 @Retryable 注解的方法保持一致,第一入?yún)橐卦嚨漠惓#渌麉?shù)與 @Retryable 保持一致,返回值也要一樣,否則無(wú)法執(zhí)行!
@Retryable(value = MyException.class)
public void testRetry06() throws MyException {
System.out.println("測(cè)試兜底方法");
throw new MyException("出現(xiàn)了異常");
}
@Recover
public void recover06(MyException e) {
System.out.println("兜底方法開啟,異常信息:" + e.getMessage());
}
熔斷模式@CircuitBreaker
指在具體的重試機(jī)制下失敗后打開斷路器,過(guò)了一段時(shí)間,斷路器進(jìn)入半開狀態(tài),允許一個(gè)進(jìn)入重試,若失敗再次進(jìn)入斷路器,成功則關(guān)閉斷路器,注解為 @CircuitBreaker ,具體包括熔斷打開時(shí)間、重置過(guò)期時(shí)間
@CircuitBreaker(openTimeout = 1000, resetTimeout = 3000, value = MyException.class)
public void testRetry07() throws MyException {
System.out.println("測(cè)試CircuitBreaker注解");
throw new MyException("出現(xiàn)了異常");
}
例:openTimeout 時(shí)間范圍內(nèi)失敗 maxAttempts 次數(shù)后,熔斷打開 resetTimeout 時(shí)長(zhǎng) 這個(gè)方法的意思就是方法在一秒內(nèi)失敗三次時(shí),觸發(fā)熔斷,下次在有請(qǐng)求過(guò)來(lái)時(shí),直接進(jìn)入
重試策略
- SimpleRetryPolicy 默認(rèn)最多重試 3 次
- TimeoutRetryPolicy 默認(rèn)在 1 秒內(nèi)失敗都會(huì)重試
- ExpressionRetryPolicy 符合表達(dá)式就會(huì)重試
- CircuitBreakerRetryPolicy 增加了熔斷的機(jī)制,如果不在熔斷狀態(tài),則允許重試
- CompositeRetryPolicy 可以組合多個(gè)重試策略
- NeverRetryPolicy 從不重試(也是一種重試策略哈)
- AlwaysRetryPolicy 總是重試
退避策略
退避策略退避是指怎么去做下一次的重試,在這里其實(shí)就是等待多長(zhǎng)時(shí)間。
通過(guò) @Backoff 注解實(shí)現(xiàn),那么我們首先看一下@Backoff 的參數(shù)
@Backoff 參數(shù)
- value
默認(rèn)為 1000, 與 delay 作用相同,表示延遲的毫秒數(shù)。當(dāng) delay 非 0 時(shí),此參數(shù)忽略。
- delay
默認(rèn)為 0。在指數(shù)情況下用作初始值,在統(tǒng)一情況下用作*的最小值。當(dāng)此元素的值為 0 時(shí),將采用元素 value 的值,否則將采用此元素的值,并且將忽略 value。
- maxDelay
默認(rèn)為 0。重試之間的最大等待時(shí)間(以毫秒為單位)。如果小于 delay,那么將應(yīng)用默認(rèn)值為 30000L
- multipler
默認(rèn)為 0。如果為正,則用作乘法器以生成下一個(gè)退避延遲。返回一個(gè)乘法器,用于計(jì)算下一個(gè)退避延遲
- delayExpression
評(píng)估標(biāo)準(zhǔn)退避期的表達(dá)式。在指數(shù)情況下用作初始值*,在均勻情況下用作最小值。覆蓋 delay。
- maxDelayExpression
該表達(dá)式計(jì)算重試之間的最大等待時(shí)間(以毫秒為單位)。 如果小于 delay,那么將應(yīng)用 30000L 為默認(rèn)值。覆蓋 maxDelay。
- multiplierExpression
評(píng)估為用作乘數(shù)的值,以生成退避的下一個(gè)延遲。覆蓋 multiplier。 返回一個(gè)乘數(shù)表達(dá)式,用于計(jì)算下一個(gè)退避延遲
- random
默認(rèn)為 false,在指數(shù)情況下 multiplier> 0 將此值設(shè)置為 true 可以使后退延遲隨機(jī)化,從而使最大延遲乘以前一延遲,并且兩個(gè)值之間的分布是均勻的。
@Retryable(value = MyException.class, maxAttempts = 4,
backoff = @Backoff(delay = 2000, multiplier = 2, maxDelay = 5000))
public void testRetry08() throws MyException {
System.out.println("測(cè)試-backoff屬性");
throw new MyException("出現(xiàn)了異常");
}
@Backoff 的參數(shù)會(huì)影響我們使用哪種退避策略
- FixedBackOffPolicy
默認(rèn)退避策略,每 1 秒重試 1 次
- ExponentialBackOffPolicy
指數(shù)退避策略,當(dāng)設(shè)置 multiplier 時(shí)使用,每次重試時(shí)間間隔為 當(dāng)前延遲時(shí)間 * multiplier。
例如:默認(rèn)初始 0.1 秒,系數(shù)是 2,那么下次延遲 0.2 秒,再下次就是延遲 0.4 秒,如此類推,最大 30 秒。
- ExponentialRandomBackOffPolicy
指數(shù)隨機(jī)退避策略。在指數(shù)退避策略的基礎(chǔ)上增加了隨機(jī)性。
- UniformRandomBackOffPolicy
均勻隨機(jī)策略,設(shè)置 maxDely 但沒(méi)有設(shè)置 multiplier 時(shí)使用,重試間隔會(huì)在 maxDelay 和 delay 間隨機(jī)
原理
切入點(diǎn)
@EnableRetry
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@EnableAspectJAutoProxy(proxyTargetClass = false)
@Import(RetryConfiguration.class)
@Documented
public @interface EnableRetry {
/**
* Indicate whether subclass-based (CGLIB) proxies are to be created as opposed to
* standard JAVA interface-based proxies. The default is {@code false}.
* @return whether to proxy or not to proxy the class
*/
boolean proxyTargetClass() default false;
}
@EnablRetry 中使用了兩個(gè)特殊的注解
- @EnableAspectJAutoProxy
這個(gè)注解的作用是開啟 aop 的功能,默認(rèn)使用 jdk 的動(dòng)態(tài)代理。如果 proxyTargetClass 參數(shù)為 true,則使用 cglib 的動(dòng)態(tài)代理。
- @Import
Import 引入了 RetryConfiguration 的 bean 。我們重點(diǎn)看下這個(gè) bean。
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
@Component
public class RetryConfiguration extends AbstractPointcutAdvisor
implements IntroductionAdvisor, BeanFactoryAware, InitializingBean {
private Advice advice;
private Pointcut pointcut;
我們可以看到 RetryConfiguration 繼承了 AbstractPointcutAdvisor,所以 RetryConfiguration 需要實(shí)現(xiàn) getAdvice() 和 getPointcut() 接口,所以這個(gè) bean 的作用就是為 @Retryable 注解注冊(cè) pointcut 切點(diǎn)和 advice 增強(qiáng)。我們?cè)賮?lái)看他的 初始化方法
@Override
public void afterPropertiesSet() throws Exception {
this.retryContextCache = findBean(RetryContextCache.class);
this.methodArgumentsKeyGenerator = findBean(MethodArgumentsKeyGenerator.class);
this.newMethodArgumentsIdentifier = findBean(NewMethodArgumentsIdentifier.class);
this.retryListeners = findBeans(RetryListener.class);
this.sleeper = findBean(Sleeper.class);
Set<Class<? extends Annotation>> retryableAnnotationTypes = new LinkedHashSet<Class<? extends Annotation>>(1);
retryableAnnotationTypes.add(Retryable.class);
this.pointcut = buildPointcut(retryableAnnotationTypes); //創(chuàng)建 pointcut
this.advice = buildAdvice(); //創(chuàng)建 advice
if (this.advice instanceof BeanFactoryAware) {
((BeanFactoryAware) this.advice).setBeanFactory(this.beanFactory);
}
}
protected Advice buildAdvice() {
AnnotationAwareRetryOperationsInterceptor interceptor = new AnnotationAwareRetryOperationsInterceptor();
if (this.retryContextCache != null) {
interceptor.setRetryContextCache(this.retryContextCache);
}
if (this.retryListeners != null) {
interceptor.setListeners(this.retryListeners);
}
if (this.methodArgumentsKeyGenerator != null) {
interceptor.setKeyGenerator(this.methodArgumentsKeyGenerator);
}
if (this.newMethodArgumentsIdentifier != null) {
interceptor.setNewItemIdentifier(this.newMethodArgumentsIdentifier);
}
if (this.sleeper != null) {
interceptor.setSleeper(this.sleeper);
}
return interceptor;
}
上面代碼用到了 AnnotationClassOrMethodPointcut,其實(shí)它最終還是用到了 AnnotationMethodMatcher 來(lái)根據(jù)注解進(jìn)行切入點(diǎn)的過(guò)濾。這里就是 @Retryable 注解了
下面來(lái)看 AnnotationAwareRetryOperationsInterceptor 的 invoke() 方法
@Override
public Object invoke(MethodInvocation invocation) throws Throwable {
//獲取真正的代理類
MethodInterceptor delegate = getDelegate(invocation.getThis(), invocation.getMethod());
if (delegate != null) {
//代理類存在,則執(zhí)行代理類的 invoke()方法
return delegate.invoke(invocation);
}
else {
//否則,直接執(zhí)行目標(biāo)方法
return invocation.proceed();
}
}
這里 getDelegate() 會(huì)處理 @Retryable 的相關(guān)參數(shù)以及決定使用哪種重試策略和退避策略。
private MethodInterceptor getDelegate(Object target, Method method) {
ConcurrentMap<Method, MethodInterceptor> cachedMethods = this.delegates.get(target);
if (cachedMethods == null) {
cachedMethods = new ConcurrentHashMap<Method, MethodInterceptor>();
}
MethodInterceptor delegate = cachedMethods.get(method);
if (delegate == null) {
//獲取方法上的 Retryable 注解
MethodInterceptor interceptor = NULL_INTERCEPTOR;
Retryable retryable = AnnotatedElementUtils.findMergedAnnotation(method, Retryable.class);
if (retryable == null) {
//獲取類上的 Retryable 注解
retryable = AnnotatedElementUtils.findMergedAnnotation(method.getDeclaringClass(), Retryable.class);
}
if (retryable == null) {
//獲取目標(biāo)類或者方法上的 Retryable 注解
retryable = findAnnotationOnTarget(target, method, Retryable.class);
}
if (retryable != null) {
if (StringUtils.hasText(retryable.interceptor())) {
//是否實(shí)現(xiàn)了自定義攔截,優(yōu)先級(jí)最高
interceptor = this.beanFactory.getBean(retryable.interceptor(), MethodInterceptor.class);
}
else if (retryable.stateful()) {
//有狀態(tài)的攔截
interceptor = getStatefulInterceptor(target, method, retryable);
}
else {
//無(wú)狀態(tài)的攔截
interceptor = getStatelessInterceptor(target, method, retryable);
}
}
cachedMethods.putIfAbsent(method, interceptor);
delegate = cachedMethods.get(method);
}
this.delegates.putIfAbsent(target, cachedMethods);
return delegate == NULL_INTERCEPTOR ? null : delegate;
}
該方法會(huì)返回 @Retryable 最終使用的處理類,我們重點(diǎn)看一下 getStatelessInterceptor 的處理,getStatefulInterceptor 中多了 @CircuitBreaker 熔斷相關(guān)的處理。
private MethodInterceptor getStatelessInterceptor(Object target, Method method, Retryable retryable) {
//生成 RetryTemplate,同時(shí)主持 listener
RetryTemplate template = createTemplate(retryable.listeners());
//設(shè)置重試策略
template.setRetryPolicy(getRetryPolicy(retryable));
//設(shè)置退避策略
template.setBackOffPolicy(getBackoffPolicy(retryable.backoff()));
//通過(guò) StatelessRetryInterceptorBuilder 創(chuàng)建 RetryOperationsInterceptor 攔截,初始化重試模板等信息
return RetryInterceptorBuilder.stateless().retryOperations(template).label(retryable.label())
.recoverer(getRecoverer(target, method)).build();
}
在回頭看看 getStatefulInterceptor 方法
private MethodInterceptor getStatefulInterceptor(Object target, Method method, Retryable retryable) {
RetryTemplate template = createTemplate(retryable.listeners());
template.setRetryContextCache(this.retryContextCache);
//獲取方法上的 CircuitBreaker 注解
CircuitBreaker circuit = AnnotatedElementUtils.findMergedAnnotation(method, CircuitBreaker.class);
if (circuit == null) {
//如果熔斷參數(shù)不為空,則處理相關(guān)參數(shù),返回響應(yīng)的攔截處理方,如果為空 ,則處理非熔斷的有狀態(tài)重試
circuit = findAnnotationOnTarget(target, method, CircuitBreaker.class);
}
if (circuit != null) {
//處理 CircuitBreaker 注解中的 retryable 相關(guān)參數(shù),獲得重試策略
RetryPolicy policy = getRetryPolicy(circuit);
CircuitBreakerRetryPolicy breaker = new CircuitBreakerRetryPolicy(policy);
breaker.setOpenTimeout(getOpenTimeout(circuit));
breaker.setResetTimeout(getResetTimeout(circuit));
template.setRetryPolicy(breaker);
template.setBackOffPolicy(new NoBackOffPolicy());
String label = circuit.label();
if (!StringUtils.hasText(label)) {
label = method.toGenericString();
}
return RetryInterceptorBuilder.circuitBreaker().keyGenerator(new FixedKeyGenerator("circuit"))
.retryOperations(template).recoverer(getRecoverer(target, method)).label(label).build();
}
RetryPolicy policy = getRetryPolicy(retryable);
template.setRetryPolicy(policy);
template.setBackOffPolicy(getBackoffPolicy(retryable.backoff()));
String label = retryable.label();
return RetryInterceptorBuilder.stateful().keyGenerator(this.methodArgumentsKeyGenerator)
.newMethodArgumentsIdentifier(this.newMethodArgumentsIdentifier).retryOperations(template).label(label)
.recoverer(getRecoverer(target, method)).build();
}
重試邏輯及策略實(shí)現(xiàn)
RetryTemplate 的 doExecute 方法。
protected <T, E extends Throwable> T doExecute(RetryCallback<T, E> retryCallback,
RecoveryCallback<T> recoveryCallback, RetryState state)
throws E, ExhaustedRetryException {
// 獲得重試策略
RetryPolicy retryPolicy = this.retryPolicy;
// 退避策略
BackOffPolicy backOffPolicy = this.backOffPolicy;
//新建一個(gè) RetryContext 來(lái)保存本輪重試的上下文,允許重試策略自行初始化
RetryContext context = open(retryPolicy, state);
if (this.logger.isTraceEnabled()) {
this.logger.trace("RetryContext retrieved: " + context);
}
// Make sure the context is available globally for clients who need
// it...
RetrySynchronizationManager.register(context);
Throwable lastException = null;
boolean exhausted = false;
try {
//給監(jiān)聽器發(fā)送一條信息。
boolean running = doOpenInterceptors(retryCallback, context);
if (!running) {
throw new TerminatedRetryException(
"Retry terminated abnormally by interceptor before first attempt");
}
// Get or Start the backoff context...
BackOffContext backOffContext = null;
Object resource = context.getAttribute("backOffContext");
if (resource instanceof BackOffContext) {
backOffContext = (BackOffContext) resource;
}
if (backOffContext == null) {
backOffContext = backOffPolicy.start(context);
if (backOffContext != null) {
context.setAttribute("backOffContext", backOffContext);
}
}
//判斷能否重試,就是調(diào)用 RetryPolicy 的 canRetry 方法來(lái)判斷。
//這個(gè)循環(huán)會(huì)直到原方法不拋出異常,或不需要再重試
while (canRetry(retryPolicy, context) && !context.isExhaustedOnly()) {
try {
if (this.logger.isDebugEnabled()) {
this.logger.debug("Retry: count=" + context.getRetryCount());
}
lastException = null;
return retryCallback.doWithRetry(context);
}
catch (Throwable e) {
//方法拋出了異常
lastException = e;
try {
//記錄異常信息
registerThrowable(retryPolicy, state, context, e);
}
catch (Exception ex) {
throw new TerminatedRetryException("Could not register throwable",
ex);
}
finally {
//調(diào)用 RetryListener 的 onError 方法
doOnErrorInterceptors(retryCallback, context, e);
}
//再次判斷能否重試
if (canRetry(retryPolicy, context) && !context.isExhaustedOnly()) {
try {
//如果可以重試則走退避策略
backOffPolicy.backOff(backOffContext);
}
catch (BackOffInterruptedException ex) {
lastException = e;
// back off was prevented by another thread - fail the retry
if (this.logger.isDebugEnabled()) {
this.logger
.debug("Abort retry because interrupted: count="
+ context.getRetryCount());
}
throw ex;
}
}
if (this.logger.isDebugEnabled()) {
this.logger.debug(
"Checking for rethrow: count=" + context.getRetryCount());
}
if (shouldRethrow(retryPolicy, context, state)) {
if (this.logger.isDebugEnabled()) {
this.logger.debug("Rethrow in retry for policy: count="
+ context.getRetryCount());
}
throw RetryTemplate.<E>wrapIfNecessary(e);
}
}
/*
* A stateful attempt that can retry may rethrow the exception before now,
* but if we get this far in a stateful retry there's a reason for it,
* like a circuit breaker or a rollback classifier.
*/
if (state != null && context.hasAttribute(GLOBAL_STATE)) {
break;
}
}
if (state == null && this.logger.isDebugEnabled()) {
this.logger.debug(
"Retry failed last attempt: count=" + context.getRetryCount());
}
exhausted = true;
//這里會(huì)查看是否有兜底方法,有就執(zhí)行,沒(méi)有就拋出異常
return handleRetryExhausted(recoveryCallback, context, state);
}
catch (Throwable e) {
throw RetryTemplate.<E>wrapIfNecessary(e);
}
finally {
close(retryPolicy, context, state, lastException == null || exhausted);
//關(guān)閉 RetryListener
doCloseInterceptors(retryCallback, context, lastException);
RetrySynchronizationManager.clear();
}
}
主要核心重試邏輯就是上面的代碼了,看上去還是挺簡(jiǎn)單的。下面看 RetryPolicy 的 canRetry 方法和 BackOffPolicy 的 backOff 方法,以及這兩個(gè) Policy 是怎么來(lái)的。我們回頭看看getStatelessInterceptor方法中的getRetryPolicy和getRetryPolicy方法。
private RetryPolicy getRetryPolicy(Annotation retryable) {
Map<String, Object> attrs = AnnotationUtils.getAnnotationAttributes(retryable);
@SuppressWarnings("unchecked")
Class<? extends Throwable>[] includes = (Class<? extends Throwable>[]) attrs.get("value");
//通過(guò)注解屬性判斷重試策略 這里判斷如果 value 注解內(nèi)容為空才去獲取 include 注解的內(nèi)容 可得出 value 的優(yōu)先級(jí)大于 include
String exceptionExpression = (String) attrs.get("exceptionExpression");
boolean hasExpression = StringUtils.hasText(exceptionExpression);
if (includes.length == 0) {
@SuppressWarnings("unchecked")
Class<? extends Throwable>[] value = (Class<? extends Throwable>[]) attrs.get("include");
includes = value;
}
@SuppressWarnings("unchecked")
Class<? extends Throwable>[] excludes = (Class<? extends Throwable>[]) attrs.get("exclude");
Integer maxAttempts = (Integer) attrs.get("maxAttempts");
String maxAttemptsExpression = (String) attrs.get("maxAttemptsExpression");
if (StringUtils.hasText(maxAttemptsExpression)) {
maxAttempts = PARSER.parseExpression(resolve(maxAttemptsExpression), PARSER_CONTEXT)
.getValue(this.evaluationContext, Integer.class);
}
if (includes.length == 0 && excludes.length == 0) {
SimpleRetryPolicy simple = hasExpression ? new ExpressionRetryPolicy(resolve(exceptionExpression))
.withBeanFactory(this.beanFactory)
: new SimpleRetryPolicy();
simple.setMaxAttempts(maxAttempts);
return simple;
}
Map<Class<? extends Throwable>, Boolean> policyMap = new HashMap<Class<? extends Throwable>, Boolean>();
for (Class<? extends Throwable> type : includes) {
policyMap.put(type, true);
}
for (Class<? extends Throwable> type : excludes) {
policyMap.put(type, false);
}
boolean retryNotExcluded = includes.length == 0;
if (hasExpression) {
return new ExpressionRetryPolicy(maxAttempts, policyMap, true, exceptionExpression, retryNotExcluded)
.withBeanFactory(this.beanFactory);
}
else {
return new SimpleRetryPolicy(maxAttempts, policyMap, true, retryNotExcluded);
}
}
總結(jié)一下:就是通過(guò) @Retryable 注解中的參數(shù),來(lái)判斷具體使用文章開頭說(shuō)到的哪個(gè)重試策略,是 SimpleRetryPolicy 還是 ExpressionRetryPolicy 等。
private BackOffPolicy getBackoffPolicy(Backoff backoff) {
long min = backoff.delay() == 0 ? backoff.value() : backoff.delay();
if (StringUtils.hasText(backoff.delayExpression())) {
min = PARSER.parseExpression(resolve(backoff.delayExpression()), PARSER_CONTEXT)
.getValue(this.evaluationContext, Long.class);
}
long max = backoff.maxDelay();
if (StringUtils.hasText(backoff.maxDelayExpression())) {
max = PARSER.parseExpression(resolve(backoff.maxDelayExpression()), PARSER_CONTEXT)
.getValue(this.evaluationContext, Long.class);
}
double multiplier = backoff.multiplier();
if (StringUtils.hasText(backoff.multiplierExpression())) {
multiplier = PARSER.parseExpression(resolve(backoff.multiplierExpression()), PARSER_CONTEXT)
.getValue(this.evaluationContext, Double.class);
}
if (multiplier > 0) {
ExponentialBackOffPolicy policy = new ExponentialBackOffPolicy();
if (backoff.random()) {
policy = new ExponentialRandomBackOffPolicy();
}
policy.setInitialInterval(min);
policy.setMultiplier(multiplier);
policy.setMaxInterval(max > min ? max : ExponentialBackOffPolicy.DEFAULT_MAX_INTERVAL);
if (this.sleeper != null) {
policy.setSleeper(this.sleeper);
}
return policy;
}
if (max > min) {
UniformRandomBackOffPolicy policy = new UniformRandomBackOffPolicy();
policy.setMinBackOffPeriod(min);
policy.setMaxBackOffPeriod(max);
if (this.sleeper != null) {
policy.setSleeper(this.sleeper);
}
return policy;
}
FixedBackOffPolicy policy = new FixedBackOffPolicy();
policy.setBackOffPeriod(min);
if (this.sleeper != null) {
policy.setSleeper(this.sleeper);
}
return policy;
}
就是通過(guò) @Backoff 注解中的參數(shù),來(lái)判斷具體使用文章開頭說(shuō)到的哪個(gè)退避策略,是 FixedBackOffPolicy 還是 UniformRandomBackOffPolicy 等。
那么每個(gè) RetryPolicy 都會(huì)重寫 canRetry 方法,然后在 RetryTemplate 判斷是否需要重試。我們看看 SimpleRetryPolicy 的
@Override
public boolean canRetry(RetryContext context) {
Throwable t = context.getLastThrowable();
//判斷拋出的異常是否符合重試的異常
//還有,是否超過(guò)了重試的次數(shù)
return (t == null || retryForException(t)) && context.getRetryCount() < maxAttempts;
}
同樣,我們看看 FixedBackOffPolicy 的退避方法。
protected void doBackOff() throws BackOffInterruptedException {
try {
//就是 sleep 固定的時(shí)間
sleeper.sleep(backOffPeriod);
}
catch (InterruptedException e) {
throw new BackOffInterruptedException("Thread interrupted while sleeping", e);
}
}
至此,重試的主要原理以及邏輯大概就是這樣了。
作者:穆清
來(lái)源:微信公眾號(hào):政采云技術(shù)
出處
:https://mp.weixin.qq.com/s/fCogDuNUz74EHd_WEYqfZA