前言

Spring实现了一套重试机制,功能简单实用。Spring Retry是从Spring Batch 2.2.0版本中独立出来,变成了Spring Retry模块。,已经广泛应用于Spring Batch,Spring Integration, Spring for Apache Hadoop等Spring项目。

使用Spring Retry

(一)Maven依赖

 <!-- 重试机制 --><dependency><groupId>org.springframework.retry</groupId><artifactId>spring-retry</artifactId><version>1.2.4.RELEASE</version></dependency><dependency><groupId>org.aspectj</groupId><artifactId>aspectjweaver</artifactId><version>1.9.4</version>
</dependency>

(二)配置类添加注解 @EnableRetry

@EnableRetry
@Configuration
public class RetryConfiguration {}

(三)Service方法编写使用以下2个注解,@Retryable标记方法重试的相关信息和@Recover标记重试失败的回调的方法

@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Retryable {String interceptor() default "";Class<? extends Throwable>[] value() default {};Class<? extends Throwable>[] include() default {};Class<? extends Throwable>[] exclude() default {};String label() default "";boolean stateful() default false;int maxAttempts() default 3;String maxAttemptsExpression() default "";Backoff backoff() default @Backoff;String exceptionExpression() default "";String[] listeners() default {};
}

@Retryable注解,其中:
value : 指定发生的异常进行重试 
include : 同value , 默认"" , 当exclude也为空时 , 所有异常都重试 
exclude : 排除不重试的异常 , 默认"" , 当include也为空时 , 所有异常都重试
maxAttemps : 尝试次数 , 默认3 . 注意该值包含第一次正常执行的次数 , 即失败之后重新尝试2次 , 一共执行3次
backoff : 重试补偿机制 , 默认无 , 属性配置如下
    delay : 延迟指定时间后(单位毫秒)重试
    multiplier : 指定延迟的倍数 , 每次执行间隔加倍延迟 . 如delay=5000L,multiplier=2 , 第一次重 试为5S后 , 第二次为第一次的10S后 , 第三次为第二次的20S后

@Recover注解,其中:

当重试次数达到限定时 , 会执行@Recover注解的补偿方法 , 只有在入参与发生异常匹配时才会执行该补偿方法,即发生的异常类型需要和@Recover注解的参数一致,@Retryable注解的方法不能有返回值,不然@Recover注解的方法无效

在SpringBoot中使用

@SpringBootApplication
@EnableRetry//启用重试机制
public class RetryApplication {public static void main(String[] args) {SpringApplication.run(RetryApplication.class, args);}
}/** 出现指定异常时(RuntimeException) , 再重试3次 , 每次延迟5s , 之后每次延迟翻倍*/@Retryable(include = {RuntimeException.class}, maxAttempts = 4, backoff = @Backoff(delay = 2000L, multiplier = 2))public void work2() {System.out.println("执行方法 : " + LocalDateTime.now());throw new RuntimeException();//模拟异常}/** 当重试次数达到限定时 , 会执行@Recover注解的补偿方法 , 只有在入参与发生异常匹配时才会执行该补偿方法 */@Recoverpublic void recover(RuntimeException e) {System.out.println("执行补偿方法 : " + LocalDateTime.now());}

运行结果:

执行方法:2019-12-31T20:17:48.566

执行方法:2019-12-31T20:17:49.567

执行方法:2019-12-31T20:17:50.568

执行补偿方法:2019-12-31T20:17:50.568

常见问题

  • maxAttemps 参数为尝试次数 , 而非重试次数 . 即=3时 , 会重试2次 , =4时会重试3次 , 因为它包括了首次正常执行的计数
  • retry利用了Spring AOP的原理 , 所以底层采用了代理的技术 , 所有同一个方法内调用会使代理失效(导入的要是代理对象而非this对象)
  • 有关非幂等的操作(如新增,修改等) , 不要使用重试 , 会影响数据一致性

Spring Retry的重试策略和退避策略

通过源码可以看出,Spring Retry的重试策略主要在policy包中,常见的包括:

  • SimpleRetryPolicy 
    默认最多重试3次
  • TimeoutRetryPolicy 
    默认在1秒内失败都会重试
  • ExpressionRetryPolicy 
    符合表达式就会重试
  • CircuitBreakerRetryPolicy 
    增加了熔断的机制,如果不在熔断状态,则允许重试
  • CompositeRetryPolicy 
    可以组合多个重试策略
  • NeverRetryPolicy 
    从不重试(也是一种重试策略哈)
  • AlwaysRetryPolicy 
    总是重试

退避策略主要在backoff包里,退避是指怎么去做下一次的重试,在这里其实就是等待多长时间,主要包括:

  • FixedBackOffPolicy 
    默认固定延迟1秒后执行下一次重试
  • ExponentialBackOffPolicy 
    指数递增延迟执行重试,默认初始0.1秒,系数是2,那么下次延迟0.2秒,再下次就是延迟0.4秒,如此类推,最大30秒。
  • ExponentialRandomBackOffPolicy 
    在上面那个策略上增加随机性
  • UniformRandomBackOffPolicy 
    这个跟上面的区别就是,上面的延迟会不停递增,这个只会在固定的区间随机
  • StatelessBackOffPolicy 
    这个说明是无状态的,所谓无状态就是对上次的退避无感知,从它下面的子类也能看出来

Spring Retry的原理

代码如何实现重试的,分析@EnableRetry

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@EnableAspectJAutoProxy(proxyTargetClass = false
)
@Import({RetryConfiguration.class})
@Documented
public @interface EnableRetry {boolean proxyTargetClass() default false;
}

我们可以看到

@EnableAspectJAutoProxy(proxyTargetClass = false)就是打开Spring AOP功能,@Import注解注册RetryConfiguration这个bean,RetryConfigutation继承AbstractPointcutAdvisor,它有一个pointcut和一个advice。我们知道,在IOC过程中会根据PointcutAdvisor类来对Bean进行Pointcut的过滤,然后生成对应的AOP代理类,用advice来加强处理。

@Configuration
public class RetryConfiguration extends AbstractPointcutAdvisor implements IntroductionAdvisor, BeanFactoryAware {private Advice advice;private Pointcut pointcut;@Autowired(required = false)private RetryContextCache retryContextCache;@Autowired(required = false)private List<RetryListener> retryListeners;@Autowired(required = false)private MethodArgumentsKeyGenerator methodArgumentsKeyGenerator;@Autowired(required = false)private NewMethodArgumentsIdentifier newMethodArgumentsIdentifier;@Autowired(required = false)private Sleeper sleeper;private BeanFactory beanFactory;public RetryConfiguration() {}@PostConstructpublic void init() {Set<Class<? extends Annotation>> retryableAnnotationTypes = new LinkedHashSet(1);retryableAnnotationTypes.add(Retryable.class);this.pointcut = this.buildPointcut(retryableAnnotationTypes);this.advice = this.buildAdvice();if (this.advice instanceof BeanFactoryAware) {((BeanFactoryAware)this.advice).setBeanFactory(this.beanFactory);}}public void setBeanFactory(BeanFactory beanFactory) {this.beanFactory = beanFactory;}public ClassFilter getClassFilter() {return this.pointcut.getClassFilter();}public Class<?>[] getInterfaces() {return new Class[]{org.springframework.retry.interceptor.Retryable.class};}public void validateInterfaces() throws IllegalArgumentException {}public Advice getAdvice() {return this.advice;}public Pointcut getPointcut() {return this.pointcut;}//创建advice对象,即拦截器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;}protected Pointcut buildPointcut(Set<Class<? extends Annotation>> retryAnnotationTypes) {ComposablePointcut result = null;Iterator var3 = retryAnnotationTypes.iterator();while(var3.hasNext()) {Class<? extends Annotation> retryAnnotationType = (Class)var3.next();Pointcut filter = new RetryConfiguration.AnnotationClassOrMethodPointcut(retryAnnotationType);if (result == null) {result = new ComposablePointcut(filter);} else {result.union(filter);}}return result;}private static class AnnotationMethodsResolver {private Class<? extends Annotation> annotationType;public AnnotationMethodsResolver(Class<? extends Annotation> annotationType) {this.annotationType = annotationType;}public boolean hasAnnotatedMethods(Class<?> clazz) {final AtomicBoolean found = new AtomicBoolean(false);ReflectionUtils.doWithMethods(clazz, new MethodCallback() {public void doWith(Method method) throws IllegalArgumentException, IllegalAccessException {if (!found.get()) {Annotation annotation = AnnotationUtils.findAnnotation(method, AnnotationMethodsResolver.this.annotationType);if (annotation != null) {found.set(true);}}}});return found.get();}}private final class AnnotationClassOrMethodFilter extends AnnotationClassFilter {private final RetryConfiguration.AnnotationMethodsResolver methodResolver;AnnotationClassOrMethodFilter(Class<? extends Annotation> annotationType) {super(annotationType, true);this.methodResolver = new RetryConfiguration.AnnotationMethodsResolver(annotationType);}public boolean matches(Class<?> clazz) {return super.matches(clazz) || this.methodResolver.hasAnnotatedMethods(clazz);}}private final class AnnotationClassOrMethodPointcut extends StaticMethodMatcherPointcut {private final MethodMatcher methodResolver;AnnotationClassOrMethodPointcut(Class<? extends Annotation> annotationType) {this.methodResolver = new AnnotationMethodMatcher(annotationType);this.setClassFilter(RetryConfiguration.this.new AnnotationClassOrMethodFilter(annotationType));}public boolean matches(Method method, Class<?> targetClass) {return this.getClassFilter().matches(targetClass) || this.methodResolver.matches(method, targetClass);}public boolean equals(Object other) {if (this == other) {return true;} else if (!(other instanceof RetryConfiguration.AnnotationClassOrMethodPointcut)) {return false;} else {RetryConfiguration.AnnotationClassOrMethodPointcut otherAdvisor = (RetryConfiguration.AnnotationClassOrMethodPointcut)other;return ObjectUtils.nullSafeEquals(this.methodResolver, otherAdvisor.methodResolver);}}}
}

在初始化方法中,创建pointcut和advice的实例,其中AnnotationAwareRetryOperationsInterceptor是一个MethodInterceptor,在创建AOP代理过程中如果目标方法符合pointcut的规则,它就会加到interceptor列表中,然后做增强,我们看看invoke方法做了什么增强。

public Object invoke(MethodInvocation invocation) throws Throwable {MethodInterceptor delegate = this.getDelegate(invocation.getThis(), invocation.getMethod());return delegate != null ? delegate.invoke(invocation) : invocation.proceed();}

这里用到了委托,主要是需要根据配置委托给具体“有状态”的interceptor还是“无状态”的interceptor

private MethodInterceptor getDelegate(Object target, Method method) {      if (!this.delegates.containsKey(target) || !this.delegates.get(target).containsKey(method)) {      synchronized (this.delegates) {      if (!this.delegates.containsKey(target)) {      this.delegates.put(target, new HashMap<Method, MethodInterceptor>());      }      Map<Method, MethodInterceptor> delegatesForTarget = this.delegates.get(target);      if (!delegatesForTarget.containsKey(method)) {      Retryable retryable = AnnotationUtils.findAnnotation(method, Retryable.class);      if (retryable == null) {      retryable = AnnotationUtils.findAnnotation(method.getDeclaringClass(), Retryable.class);      }      if (retryable == null) {      retryable = findAnnotationOnTarget(target, method);      }      if (retryable == null) {      return delegatesForTarget.put(method, null);      }      MethodInterceptor delegate;      //支持自定义MethodInterceptor,而且优先级最高      if (StringUtils.hasText(retryable.interceptor())) {      delegate = this.beanFactory.getBean(retryable.interceptor(), MethodInterceptor.class);      }      else if (retryable.stateful()) {      //得到“有状态”的interceptor      delegate = getStatefulInterceptor(target, method, retryable);      }      else {      //得到“无状态”的interceptor      delegate = getStatelessInterceptor(target, method, retryable);      }      delegatesForTarget.put(method, delegate);      }      }      }      return this.delegates.get(target).get(method);      }   

getStatefulInterceptor和getStatelessInterceptor都是差不多,我们先看看比较简单的getStatelessInterceptor:

private MethodInterceptor getStatelessInterceptor(Object target, Method method, Retryable retryable) {      //生成一个RetryTemplate      RetryTemplate template = createTemplate(retryable.listeners());      //生成retryPolicy      template.setRetryPolicy(getRetryPolicy(retryable));      //生成backoffPolicy      template.setBackOffPolicy(getBackoffPolicy(retryable.backoff()));      return RetryInterceptorBuilder.stateless()      .retryOperations(template)      .label(retryable.label())      .recoverer(getRecoverer(target, method))      .build();      } 

具体生成retryPolicy和backoffPolicy的规则,我们等下再来看。 RetryInterceptorBuilder其实就是为了生成 RetryOperationsInterceptor。RetryOperationsInterceptor也是一个MethodInterceptor,我们来看看它的invoke方法。

public Object invoke(final MethodInvocation invocation) throws Throwable {      String name;      if (StringUtils.hasText(label)) {      name = label;      } else {      name = invocation.getMethod().toGenericString();      }      final String label = name;      //定义了一个RetryCallback,其实看它的doWithRetry方法,调用了invocation的proceed()方法,是不是有点眼熟,这就是AOP的拦截链调用,如果没有拦截链,那就是对原来方法的调用。      RetryCallback<Object, Throwable> retryCallback = new RetryCallback<Object, Throwable>() {      public Object doWithRetry(RetryContext context) throws Exception {      context.setAttribute(RetryContext.NAME, label);      /*      * If we don't copy the invocation carefully it won't keep a reference to      * the other interceptors in the chain. We don't have a choice here but to      * specialise to ReflectiveMethodInvocation (but how often would another      * implementation come along?).      */      if (invocation instanceof ProxyMethodInvocation) {      try {      return ((ProxyMethodInvocation) invocation).invocableClone().proceed();      }      catch (Exception e) {      throw e;      }      catch (Error e) {      throw e;      }      catch (Throwable e) {      throw new IllegalStateException(e);      }      }      else {      throw new IllegalStateException(      "MethodInvocation of the wrong type detected - this should not happen with Spring AOP, " +      "so please raise an issue if you see this exception");      }      }      };      if (recoverer != null) {      ItemRecovererCallback recoveryCallback = new ItemRecovererCallback(      invocation.getArguments(), recoverer);      return this.retryOperations.execute(retryCallback, recoveryCallback);      }      //最终还是进入到retryOperations的execute方法,这个retryOperations就是在之前的builder set进来的RetryTemplate。      return this.retryOperations.execute(retryCallback);      }      

无论是RetryOperationsInterceptor还是StatufulRetryOperationsInterceptor,最终的拦截处理逻辑还是调用RetryTemplate的execute方法,RetryTemplate作为一个模板类,里面包含了重试统一逻辑。

重试逻辑及策略实现

上面介绍了Spring Retry利用了AOP代理使重试机制对业务代码进行“入侵”。继续看看重试的逻辑做了什么:
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;      //新建一个RetryContext来保存本轮重试的上下文      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 {      //如果有注册RetryListener,则会调用它的open方法,给调用者一个通知。      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);      }      }      //判断能否重试,就是调用RetryPolicy的canRetry方法来判断。      //这个循环会直到原方法不抛出异常,或不需要再重试      while (canRetry(retryPolicy, context) && !context.isExhaustedOnly()) {      try {      if (this.logger.isDebugEnabled()) {      this.logger.debug("Retry: count=" + context.getRetryCount());      }      //清除上次记录的异常      lastException = null;      //doWithRetry方法,一般来说就是原方法      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 {      //调用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;      //重试结束后如果有兜底Recovery方法则执行,否则抛异常      return handleRetryExhausted(recoveryCallback, context, state);      }      catch (Throwable e) {      throw RetryTemplate.<E>wrapIfNecessary(e);      }      finally {      //处理一些关闭逻辑      close(retryPolicy, context, state, lastException == null || exhausted);      //调用RetryListener的close方法      doCloseInterceptors(retryCallback, context, lastException);      RetrySynchronizationManager.clear();      }      }      

主要核心重试逻辑就是上面的代码了,在上面,我们漏掉了RetryPolicy的canRetry方法和BackOffPolicy的backOff方法,以及这两个Policy是怎么来的。 回头看看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");      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);      }      }      

总结:就是通过@Retryable注解中的参数,来判断具体使用文章开头说到的哪个重试策略,是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;      }      

通过@Backoff注解中的参数,来判断具体使用文章开头说到的哪个退避策略,是FixedBackOffPolicy还是UniformRandomBackOffPolicy等。那么每个RetryPolicy都会重写canRetry方法,然后在RetryTemplate判断是否需要重试。 
我们看看SimpleRetryPolicy的

@Override      public boolean canRetry(RetryContext context) {      Throwable t = context.getLastThrowable();      //判断抛出的异常是否符合重试的异常      //还有,是否超过了重试的次数      return (t == null || retryForException(t)) && context.getRetryCount() < maxAttempts;      }      

同样,我们看看FixedBackOffPolicy的退避方法。

protected void doBackOff() throws BackOffInterruptedException {      try {      //就是sleep固定的时间      sleeper.sleep(backOffPeriod);      }      catch (InterruptedException e) {      throw new BackOffInterruptedException("Thread interrupted while sleeping", e);      }      }

至此,重试的主要原理以及逻辑大概就是这样了。

RetryContext

每一个策略都有对应的Context。在Spring Retry里,其实每一个策略都是单例来的。我刚开始直觉是对每一个需要重试的方法都会new一个策略,这样重试策略之间才不会产生冲突,但是一想就知道这样就可能多出了很多策略对象出来,增加了使用者的负担,这不是一个好的设计。Spring Retry采用了一个更加轻量级的做法,就是针对每一个需要重试的方法只new一个上下文Context对象,然后在重试时,把这个Context传到策略里,策略再根据这个Context做重试,而且Spring Retry还对这个Context做了cache。这样就相当于对重试的上下文做了优化。

总结

Spring Retry通过AOP机制来实现对业务代码的重试”入侵“,RetryTemplate中包含了核心的重试逻辑,还提供了丰富的重试策略和退避策略。

SpringRetry重试机制相关推荐

  1. SpringRetry重试机制(3秒上手)

    目录 前言 一.SpringRetry的使用 1.1 引入依赖 1.2 开启重新机制 1.3 3 在方法上添加@Retryable 1.3.4 编写重试失败后的执行的方法 测试 前言 SpringRe ...

  2. springboot 整合retry(重试机制)

    当我们调用一个接口可能由于网络等原因造成第一次失败,再去尝试就成功了,这就是重试机制,spring支持重试机制,并且在Spring Cloud中可以与Hystaix结合使用,可以避免访问到已经不正常的 ...

  3. Spring-Retry重试实现原理

    点击上方蓝色"程序猿DD",选择"设为星标" 回复"资源"获取独家整理的学习资料! 作者 | Alben 来源 | http://r6d.c ...

  4. Spring Retry 重试机制实现及原理

    概要 Spring实现了一套重试机制,功能简单实用.Spring Retry是从Spring Batch独立出来的一个功能,已经广泛应用于Spring Batch,Spring Integration ...

  5. Spring Cloud Zuul重试机制探秘

    简介 本文章对应spring cloud的版本为(Dalston.SR4),具体内容如下: 开启Zuul功能 通过源码了解Zuul的一次转发 怎么开启zuul的重试机制 Edgware.RC1版本的优 ...

  6. Spring异常重试机制 - Spring Retry

    目录 一 . 引入依赖 二 . 在启用类或业务类上添加@EnableRetry注解启用重试机制(在启用类上添加全局有效 , 在业务类上添加仅当前有效) 三 . 使用@Retryable实现重试 四 . ...

  7. 【Spring Cloud】OpenFeign和Spring Cloud Loadbalancer调用失败后的重试机制比较

    1 概述 搭建一个微服务系统,有两个服务,Client和Server,Server有三个实例A.B.C,我让Client调用Server,Loadbalancer负载分担默认采用轮询机制,当Serve ...

  8. 【转载】Java重试机制

    重试机制在分布式系统中,或者调用外部接口中,都是十分重要的. 重试机制可以保护系统减少因网络波动.依赖服务短暂性不可用带来的影响,让系统能更稳定的运行的一种保护机制. 为了方便说明,先假设我们想要进行 ...

  9. Springboot 整合Retry 实现重试机制

    重试,在项目需求中是非常常见的,例如遇到网络波动等,要求某个接口或者是方法可以最多/最少调用几次: 实现重试机制,非得用Retry这个重试框架吗?那肯定不是,相信很多伙伴手写一下控制流程的逻辑也可以达 ...

最新文章

  1. NSHelper.showAlertTitle的两种用法 swift
  2. pytorch基于卷积层通道剪枝的方法
  3. 不同的二叉搜索树-战胜100%的Java用户
  4. boost::mpi模块对 all_gather() 集体的测试
  5. redis接口的二次封装
  6. Vue 组件间通信六种方式
  7. 进程与multiprocessing模块
  8. XML读取信息并显示
  9. cefsharp 网页另存为图片_如何将PDF转换为JPG图片?这些转换方法一学就会
  10. WINDOWS是如何在注册表里记录盘符分配的
  11. js基础知识学习(二)
  12. Android 服务
  13. 17素材网手动免费下载素材
  14. 实现校园网花样上网方法
  15. 去雨去雾的研究和可用方法
  16. FTP服务器的上传文件端口为,FTP服务器上传文件时的端口
  17. 爬虫之-bilibili视频下载-接口分析
  18. mysql 没有开启binlog_mysql binlog没有开启数据怎么恢复
  19. COMFAST CF-WU785AC在Ubuntu无法上网问题的解决
  20. 05.视频播放器内核切换封装

热门文章

  1. 优信与汽车之家二手车达成合作 将在交易解决方案等方面展开合作
  2. NOI 2020 游记
  3. 项目冲刺博客——第四篇
  4. 【java筑基】斗地主小游戏——Collections工具类排序功能应用
  5. CSS —— background 背景
  6. EFI和UEFI的区别
  7. vue展示信息卡片_vue组件系列-气泡卡片
  8. Java基础入门(持续更新)
  9. 多点平面方程拟合c语言,多点最小二乘法平面方程拟合计算
  10. C#,图像二值化(15)——全局阈值的一维最大熵(1D maxent)算法及源程序