SpringRetry重试机制

前言

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注解的补偿方法 , 只有在入参与发生异常匹配时才会执行该补偿方法 */
    @Recover
    public 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() {
    }

    @PostConstruct
    public 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中包含了核心的重试逻辑,还提供了丰富的重试策略和退避策略。

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值