Dubbo学习笔记8:Dubbo的集群容错与负载均衡策略

时间:2020-01-13 14:43:24   收藏:0   阅读:63

Dubbo的集群容错策略

正常情况下,当我们进行系统设计时候,不仅要考虑正常逻辑下代码该如何走,还要考虑异常情况下代码逻辑应该怎么走。当服务消费方调用服务提供方的服务出现错误时候,Dubbo提供了多种容错方案,缺省模式为failover,也就是失败重试。

Dubbo提供的集群容错模式

下面看下Dubbo提供的集群容错模式:

Failover Cluster:失败重试

当服务消费方调用服务提供者失败后自动切换到其他服务提供者服务器进行重试。这通常用于读操作或者具有幂等的写操作,需要注意的是重试会带来更长延迟。可通过 retries="2" 来设置重试次数(不含第一次)。

接口级别配置重试次数方法 <dubbo:reference retries="2" /> ,如上配置当服务消费方调用服务失败后,会再重试两次,也就是说最多会做三次调用,这里的配置对该接口的所有方法生效。当然你也可以针对某个方法配置重试次数如下:

 

<dubbo:reference>
    <dubbo:method name="sayHello" retries="2" />
</dubbo:reference>

 

Failfast Cluster:快速失败

当服务消费方调用服务提供者失败后,立即报错,也就是只调用一次。通常这种模式用于非幂等性的写操作。

Failsafe Cluster:失败安全

当服务消费者调用服务出现异常时,直接忽略异常。这种模式通常用于写入审计日志等操作。

Failback Cluster:失败自动恢复

当服务消费端用服务出现异常后,在后台记录失败的请求,并按照一定的策略后期再进行重试。这种模式通常用于消息通知操作。

Forking Cluster:并行调用

当消费方调用一个接口方法后,Dubbo Client会并行调用多个服务提供者的服务,只要一个成功即返回。这种模式通常用于实时性要求较高的读操作,但需要浪费更多服务资源。可通过 forks="2" 来设置最大并行数。

Broadcast Cluster:广播调用

当消费者调用一个接口方法后,Dubbo Client会逐个调用所有服务提供者,任意一台调用异常则这次调用就标志失败。这种模式通常用于通知所有提供者更新缓存或日志等本地资源信息。

如上,Dubbo本身提供了丰富的集群容错模式,但是如果您有定制化需求,可以根据Dubbo提供的扩展接口Cluster进行定制。在后面的消费方启动流程章节会讲解何时/如何使用的集群容错。

失败重试策略实现分析

Dubbo中具体实现失败重试的是FailoverClusterInvoker类,这里我们看下具体实现,主要看下doInvoke代码:

技术分享图片
public Result doInvoke(Invocation invocation,final List<Invoker<T>> invokers,LoadBalance loadbalance) throws RpcException{
    // (1) 所有服务提供者
    List<Invoker<T>> copyinvokers = invokers;
    checkInvokers(copyinvokers,invocation);

    // (2)获取重试次数
    int len  = getUrl().getMethodParameter(invocation.getMethodName(),Constants.RETRIES_KEY,Constants.DEFAULT_RETRIES) + 1;
    if(len <= 0){
        len = 1;
    }
    
    // (3)使用循环,失败重试
    RpcException le = null;    // last exception
    List<Invoker<T>> invoked = new ArrayList<Invoker<T>>(copyinvokers.size());
    Set<String> providers = new HashSet<String>();
    for(int i=0;i<len;i++){
        // 重试时,进行重新选择,避免重试时invoker列表已发生变化
        // 注意:如果列表发生了变化,那么invoked判断会失效,因为invoker示例已经改变
        if(i > 0){
            // (3.1)
            checkWhetherDestroyed();    // 如果当前实例已经被销毁,则抛出异常
            // (3.2) 重新获取所有服务提供者
            copyinvokers = list(invocation);
            // (3.3) 重新检查一下
            checkInvokers(copyinvokers,invocation);
        }
        // (3.4) 选择负载均衡策略
        Invoker<T> invoker = select(loadbalance,invocation,copyinvokers,invoked);
        invoked.add(invoker);
        RpcContext.getContext().setInvokers((List)invoked);
        // (3.5) 具体发起远程调用
        try{
            Result result = invoker.invoke(invocation);
            if(le != null && logger.isWarnEnabled()){
                ...
            }
            return result;
        }catch(RpcException e){
            if(e.isBiz()){    // biz exception
                throw e;
            }
            le = e;
        }catch(Throwable e){
            le = new RpcException(e.getMessage(),e);
        }finally{
            providers.add(invoker.getUrl().getAddress());
        }
    }
    throw new RpcException("抛出异常...");
}
技术分享图片

 Dubbo的负载均衡策略

当服务提供方是集群的时候,为了避免大量请求一直落到一个或几个服务提供方机器上,从而使这些机器负载很高,甚至打死,需要做一定的负载均衡策略。Dubbo提供了多种均衡策略,缺省为random,也就是每次随机调用一台服务提供者的机器。

Dubbo提供的负载均衡策略

一致性Hash负载均衡策略原理

在解决分布式系统中,负载均衡的问题可以使用Hash算法让固定的一部分请求落到同一台服务器上,这样每台服务器固定处理一部分请求(并维护这些请求的信息),起到负载均衡的作用。

但是普通的余数Hash(Hash(比如用户id)%服务器机器数)算法伸缩性很差,当新增或者下线服务器机器时候,用户id与服务器的映射关系会大量失效。一致性Hash则利用Hash环对其进行了改进。

一致性Hash概述

为了能直观的理解一致性Hash的原理,这里结合一个简单的例子来讲解,假设有4台服务器,地址为ip1/ip2/ip3/ip4 。

技术分享图片

技术分享图片

如上图可知user1 / user2的请求会落到服务器 ip2 进行处理,user3的请求会落到服务器ip3进行处理,user4的请求会落到服务器ip4进行处理,user5 / user6 的请求会落到服务器ip1进行处理。

下面考虑当ip2的服务器挂了的时候会出现什么情况?

当ip2的服务器挂了的时候,一致性Hash环大致如下图:

技术分享图片

根据顺时针规则可知user1 / user2的请求会被服务器ip3进行处理,而其他用户的请求对应的处理服务器不变,也就是只有之前被ip2处理的一部分用户的映射关系被破坏了,并且其负责处理的请求被顺时针下一个节点委托处理。

下面考虑当有新增机器加入时会出现什么情况?

当新增一个ip5的服务器后,一致性Hash环大致如下图:

技术分享图片

根据顺时针规则可知之前user1的请求应该被ip1服务器处理,现在被新增的ip5服务器处理,其他用户的请求处理服务器不变,也就是新增的服务器顺时针最近的服务器的一部分请求会被新增的服务器所替代。

一致性Hash的特性

一致性Hash有要有以下特性:

技术分享图片

服务器ip1 / ip2 / ip3经过Hash后落到了一致性Hash环上,从图中Hash值分布可知ip1会负责处理大概80%的请求,而ip2和ip3则只会负责处理大概20%的请求,虽然三个机器都在处理请求,但是明显每个机器的负载不均衡,这样称为一致性Hash的倾斜,虚拟节点的出现就是为了解决这个问题。

虚拟节点

当服务器节点比较少的时候会出现上节所说的一致性Hash倾斜的问题,一个解决方法是多加机器,但是加机器是有成本的,那么就加虚拟节点,比如上面三个机器,每个机器引入1个虚拟节点后的一致性Hash环如下图所示:

技术分享图片

 

其中ip1-1是ip1的虚拟节点,ip2-1是ip2的虚拟节点,ip3-1是ip3的虚拟节点。

可知当物理机器数目为M,虚拟节点为N的时候,实际hash环上节点个数为 M*(N+1) 。比如当客户端计算的Hash值处于ip2和ip3或者处于ip2-1和ip3-1之间时候使用ip3服务器进行处理。

均匀一致性Hash

上节我们使用虚拟节点后的图看起来比较均衡,但是如果生成虚拟节点的算法不够好很可能会得到下面的环:

技术分享图片

 

可知每个服务节点引入1个虚拟节点后,情况相比没有引入前均衡性有所改善,但是并不均衡。

均衡的一致性Hash应该如下图所示:

 技术分享图片

均匀一致性Hash的目标是如果服务器有N台,客户端的Hash值有M个,那么每个服务器应该处理大概 M/N 个用户的请求。也就是每台服务器均衡尽量均衡。Dubbo提供的一致性Hash就是不均匀的,这个大家可以去研究下ConsistentHashLoadBalance类。

原文:https://www.cnblogs.com/cnndevelop/p/12186960.html

评论(0
© 2014 bubuko.com 版权所有 - 联系我们:wmxa8@hotmail.com
打开技术之扣,分享程序人生!