日日操夜夜添-日日操影院-日日草夜夜操-日日干干-精品一区二区三区波多野结衣-精品一区二区三区高清免费不卡

公告:魔扣目錄網(wǎng)為廣大站長(zhǎng)提供免費(fèi)收錄網(wǎng)站服務(wù),提交前請(qǐng)做好本站友鏈:【 網(wǎng)站目錄:http://www.ylptlb.cn 】, 免友鏈快審服務(wù)(50元/站),

點(diǎn)擊這里在線咨詢客服
新站提交
  • 網(wǎng)站:51998
  • 待審:31
  • 小程序:12
  • 文章:1030137
  • 會(huì)員:747

什么是重試

重試是指,當(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

分享到:
標(biāo)簽:Spring
用戶無(wú)頭像

網(wǎng)友整理

注冊(cè)時(shí)間:

網(wǎng)站:5 個(gè)   小程序:0 個(gè)  文章:12 篇

  • 51998

    網(wǎng)站

  • 12

    小程序

  • 1030137

    文章

  • 747

    會(huì)員

趕快注冊(cè)賬號(hào),推廣您的網(wǎng)站吧!
最新入駐小程序

數(shù)獨(dú)大挑戰(zhàn)2018-06-03

數(shù)獨(dú)一種數(shù)學(xué)游戲,玩家需要根據(jù)9

答題星2018-06-03

您可以通過(guò)答題星輕松地創(chuàng)建試卷

全階人生考試2018-06-03

各種考試題,題庫(kù),初中,高中,大學(xué)四六

運(yùn)動(dòng)步數(shù)有氧達(dá)人2018-06-03

記錄運(yùn)動(dòng)步數(shù),積累氧氣值。還可偷

每日養(yǎng)生app2018-06-03

每日養(yǎng)生,天天健康

體育訓(xùn)練成績(jī)?cè)u(píng)定2018-06-03

通用課目體育訓(xùn)練成績(jī)?cè)u(píng)定