Ribbon架构解析之RetryHandler重试处理器

前言
在分布式场景中,调用第三方接口会因为网络延迟、异常导致调用的服务出错,重试几次可能就会调用成功,是提高结果正确性的一种有效手段。重试机制最简单呢理解为try-catch-redo模式,但是优雅的重试也是有要求的,至少应该满足如下要求:

  • 无侵入:不改动当前的业务逻辑,对于需要重试的地方,可以很简单的实现
  • 可配置:包括重试次数,重试的间隔时间,是否使用异步方式等
  • 通用性:最好是无改动(或者很小改动)的支持绝大部分的场景

重试固然重要,但不是什么场景下都适合重试的,并且重试在生产环境中需要慎用。对于重试是有场景限制的,比如参数校验不合法、写操作等(要考虑写是否幂等)都不适合重试。远程调用超时、网络突然中断可以重试。在微服务治理框架中,通常都有自己的重试与超时配置,Ribbon自然也有重试的能力。

RetryHandler重试处理器

重试,是类似于Ribbon这种组件里特别重要的概念,因此此接口特别的重要。它负责对执行时若发生异常时的一个处理接口:重试or让异常继续抛出。

public interface RetryHandler {

	public static final RetryHandler DEFAULT = new DefaultLoadBalancerRetryHandler();

	// 该异常是否可处理(可重试)
	// sameServer:true表示在同一台机器上重试。否则去其它机器重试
	public boolean isRetriableException(Throwable e, boolean sameServer);
	// 是否是Circuit熔断类型异常。比如java.net.ConnectException就属于这种故障
	// 这种异常类型一般属于比较严重的,发生的次数多了就会把它熔断(下次不会再找它了)
	public boolean isCircuitTrippingException(Throwable e);


	// 要在一台服务器上执行的最大重试次数
	public int getMaxRetriesOnSameServer();
	// 要重试的最大不同服务器数。2表示最多去2台不同的服务器身上重试
	public int getMaxRetriesOnNextServer();
}

在这里插入图片描述
我们先来看 DefaultLoadBalancerRetryHandler
在这里插入图片描述
再来看看 RequestSpecificRetryHandler

public class RequestSpecificRetryHandler implements RetryHandler {

	// fallback默认使用的是RetryHandler.DEFAULT
	// 有点代理的意思
    private final RetryHandler fallback;
    
    private int retrySameServer = -1;
    private int retryNextServer = -1;
    
    // 只有是连接异常,也就是SocketException或者其子类异常才执行重试
    private final boolean okToRetryOnConnectErrors;
    // 若是true:只要异常了,任何错都执行重试
    private final boolean okToRetryOnAllErrors;
    protected List<Class<? extends Throwable>> connectionRelated = Lists.newArrayList(SocketException.class);
    public boolean isConnectionException(Throwable e) {
        return Utils.isPresentAsCause(e, connectionRelated);
    }

	// 构造器为属性赋值。requestConfig可以是单独的,若没指定就使用默认全局的
    public RequestSpecificRetryHandler(boolean okToRetryOnConnectErrors, boolean okToRetryOnAllErrors, RetryHandler baseRetryHandler, @Nullable IClientConfig requestConfig) {
        Preconditions.checkNotNull(baseRetryHandler);
        this.okToRetryOnConnectErrors = okToRetryOnConnectErrors;
        this.okToRetryOnAllErrors = okToRetryOnAllErrors;
        this.fallback = baseRetryHandler;
        if (requestConfig != null) {
            if (requestConfig.containsProperty(CommonClientConfigKey.MaxAutoRetries)) {
                retrySameServer = requestConfig.get(CommonClientConfigKey.MaxAutoRetries); 
            }
            if (requestConfig.containsProperty(CommonClientConfigKey.MaxAutoRetriesNextServer)) {
                retryNextServer = requestConfig.get(CommonClientConfigKey.MaxAutoRetriesNextServer); 
            } 
        }
    }
}
  @Override
    public boolean isRetriableException(Throwable e, boolean sameServer) {
    	// 若强制开启所有错误都重试,那就没啥好说的
    	// 此参数默认是false,只能通过构造器来指定其值
        if (okToRetryOnAllErrors) {
            return true;
        } 

		// ClientException属于执行过程中会抛出的异常类型,所以需要加以判断
        else if (e instanceof ClientException) {
            ClientException ce = (ClientException) e;
            // 若是服务端异常,那就同一台Server上不用重试了,没要求是同一台Server才允许其重试
            if (ce.getErrorType() == ClientException.ErrorType.SERVER_THROTTLED) {
                return !sameServer;
			// 若不是服务端异常类型,那就换台Server都不用重试了
            } else {
                return false;
            }
        } 
        // 若不是ClientException,那就看看异常是否是Socket的链接异常喽
        // okToRetryOnConnectErrors的值也是由构造的时候指定的
        // 没有默认值....
        else  {
            return okToRetryOnConnectErrors && isConnectionException(e);
        }
    }
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 4
    评论
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值