背景

Dubbo是一个分布式服务框架,能避免单点故障和支持服务的横向扩容。一个服务通常会部署多个实例。如何从多个服务 Provider 组成的集群中挑选出一个进行调用,就涉及到一个负载均衡的策略。

几个概念

在讨论负载均衡之前,我想先解释一下这3个概念。

  1. 负载均衡
  2. 集群容错
  3. 服务路由

这3个概念容易混淆。他们都描述了怎么从多个 Provider 中选择一个来进行调用。那他们到底有什么区别呢?下面我来举一个简单的例子,把这几个概念阐述清楚吧。

有一个Dubbo的用户服务,在北京部署了10个,在上海部署了20个。一个杭州的服务消费方发起了一次调用,然后发生了以下的事情:

  1. 根据配置的路由规则,如果杭州发起的调用,会路由到比较近的上海的20个 Provider。
  2. 根据配置的随机负载均衡策略,在20个 Provider 中随机选择了一个来调用,假设随机到了第7个 Provider。
  3. 结果调用第7个 Provider 失败了。
  4. 根据配置的Failover集群容错模式,重试其他服务器。
  5. 重试了第13个 Provider,调用成功。

上面的第1,2,4步骤就分别对应了路由,负载均衡和集群容错。 Dubbo中,先通过路由,从多个 Provider 中按照路由规则,选出一个子集。再根据负载均衡从子集中选出一个 Provider 进行本次调用。如果调用失败了,根据集群容错策略,进行重试或定时重发或快速失败等。 可以看到Dubbo中的路由,负载均衡和集群容错发生在一次RPC调用的不同阶段。最先是路由,然后是负载均衡,最后是集群容错。 本文档只讨论负载均衡,路由和集群容错在其他的文档中进行说明。

Dubbo内置负载均衡策略

Dubbo内置了4种负载均衡策略:

  1. RandomLoadBalance:随机负载均衡。随机的选择一个。是Dubbo的默认负载均衡策略。
  2. RoundRobinLoadBalance:轮询负载均衡。轮询选择一个。
  3. LeastActiveLoadBalance:最少活跃调用数,相同活跃数的随机。活跃数指调用前后计数差。使慢的 Provider 收到更少请求,因为越慢的 Provider 的调用前后计数差会越大。
  4. ConsistentHashLoadBalance:一致性哈希负载均衡。相同参数的请求总是落在同一台机器上。

1.随机负载均衡

顾名思义,随机负载均衡策略就是从多个 Provider 中随机选择一个。但是 Dubbo 中的随机负载均衡有一个权重的概念,即按照权重设置随机概率。比如说,有10个 Provider,并不是说,每个 Provider 的概率都是一样的,而是要结合这10个 Provider 的权重来分配概率。

Dubbo中,可以对 Provider 设置权重。比如机器性能好的,可以设置大一点的权重,性能差的,可以设置小一点的权重。权重会对负载均衡产生影响。可以在Dubbo Admin中对 Provider 进行权重的设置。

基于权重的负载均衡算法

随机策略会先判断所有的 Invoker 的权重是不是一样的,如果都是一样的,那么处理就比较简单了。使用random.nexInt(length)就可以随机生成一个 Invoker 的序号,根据序号选择对应的 Invoker 。如果没有在Dubbo Admin中对服务 Provider 设置权重,那么所有的 Invoker 的权重就是一样的,默认是100。 如果权重不一样,那就需要结合权重来设置随机概率了。算法大概如下: 假如有4个 Invoker。

invoker weight
A 10
B 20
C 20
D 30

A,B,C和D总的权重是10 + 20 + 20 + 30 = 80。将80个数分布在如下的图中:

+-----------------------------------------------------------------------------------+
| | | | |
+-----------------------------------------------------------------------------------+
1 10 30 50 80 |-----A----|---------B----------|----------C---------|---------------D--------------| ---------------------15 -------------------------------------------37 -----------------------------------------------------------54

上面的图中一共有4块区域,长度分别是A,B,C和D的权重。使用random.nextInt(10 + 20 + 20 + 30),从80个数中随机选择一个。然后再判断该数分布在哪个区域。比如,如果随机到37,37是分布在C区域的,那么就选择 Invoker C。15是在B区域,54是在D区域。

随机负载均衡源码

(有权重:在权重和的范围内生成一个随机数,遍历invoker,用权重和循环减去invoker的权重,结果小于0时的invoker被选中)

下面是随机负载均衡的源码,为了方便阅读和理解,我把无关部分都去掉了。

public class RandomLoadBalance extends AbstractLoadBalance {

    private final Random random = new Random();

    protected <T> Invoker<T> doSelect(List<Invoker<T>> invokers, URL url, Invocation invocation) {
int length = invokers.size(); // Invoker 总数
int totalWeight = 0; // 所有 Invoker 的权重的和 // 判断是不是所有的 Invoker 的权重都是一样的
// 如果权重都一样,就简单了。直接用Random生成索引就可以了。
boolean sameWeight = true;
for (int i = 0; i < length; i++) {
int weight = getWeight(invokers.get(i), invocation);
totalWeight += weight; // Sum
if (sameWeight && i > 0 && weight != getWeight(invokers.get(i - 1), invocation)) {
sameWeight = false;
}
} if (totalWeight > 0 && !sameWeight) {
// 如果不是所有的 Invoker 权重都相同,那么基于权重来随机选择。权重越大的,被选中的概率越大
int offset = random.nextInt(totalWeight);
for (int i = 0; i < length; i++) {
offset -= getWeight(invokers.get(i), invocation);
if (offset < 0) {
return invokers.get(i);
}
}
}
// 如果所有 Invoker 权重相同
return invokers.get(random.nextInt(length));
}
}

2.轮询负载均衡

轮询负载均衡,就是依次的调用所有的 Provider。和随机负载均衡策略一样,轮询负载均衡策略也有权重的概念。 轮询负载均衡算法可以让RPC调用严格按照我们设置的比例来分配。不管是少量的调用还是大量的调用。但是轮询负载均衡算法也有不足的地方,存在慢的 Provider 累积请求的问题,比如:第二台机器很慢,但没挂,当请求调到第二台时就卡在那,久而久之,所有请求都卡在调到第二台上,导致整个系统变慢。

3.最少活跃调用数负载均衡

官方解释:

最少活跃调用数,相同活跃数的随机,活跃数指调用前后计数差,使慢的机器收到更少。

这个解释好像说的不是太明白。目的是让更慢的机器收到更少的请求,但具体怎么实现的还是不太清楚。举个例子:每个服务维护一个活跃数计数器。当A机器开始处理请求,该计数器加1,此时A还未处理完成。若处理完毕则计数器减1。而B机器接受到请求后很快处理完毕。那么A,B的活跃数分别是1,0。当又产生了一个新的请求,则选择B机器去执行(B活跃数最小),这样使慢的机器A收到少的请求。

处理一个新的请求时,Consumer 会检查所有 Provider 的活跃数,如果具有最小活跃数的 Invoker 只有一个,直接返回该 Invoker:

if (leastCount == 1) {
// 如果只有一个最小则直接返回
return invokers.get(leastIndexs[0]);
}

如果最小活跃数的 Invoker 有多个,且权重不相等同时总权重大于0,这时随机生成一个权重,范围在 (0,totalWeight) 间内。最后根据随机生成的权重,来选择 Invoker。

if (! sameWeight && totalWeight > 0) {
// 如果权重不相同且权重大于0则按总权重数随机
int offsetWeight = random.nextInt(totalWeight);
// 并确定随机值落在哪个片断上
for (int i = 0; i < leastCount; i++) {
int leastIndex = leastIndexs[i];
offsetWeight -= getWeight(invokers.get(leastIndex), invocation);
if (offsetWeight <= 0)
return invokers.get(leastIndex);
}
}

4.一致性Hash算法

概述:

如果采用常用的hash(object)%N算法,那么在有机器添加或者删除后,映射关系就变了,很多原有的缓存就无法找到了

一致性hash:添加删除机器前后映射关系一致,当然,不是严格一致。实现的关键是环形Hash空间。将数据和机器都hash到环上,数据映射到顺时针离自己最近的机器中。

一致性hash单调性体现在: 
无论是新增主机还是删除主机,被影响的都是离那台主机最近的那些节点,其他节点映射关系没有影响

使用一致性 Hash 算法,让相同参数的请求总是发到同一 Provider。 当某一台 Provider 崩溃时,原本发往该 Provider 的请求,基于虚拟节点,平摊到其它 Provider,不会引起剧烈变动。 参见我另一篇:https://www.cnblogs.com/twoheads/p/10135896.html

缺省只对第一个参数Hash,如果要修改,请配置:

<dubbo:parameter key="hash.arguments" value="0,1" />

缺省用160份虚拟节点,如果要修改,请配置:

<dubbo:parameter key="hash.nodes" value="320" />

优点:一致性Hash算法可以和缓存机制配合起来使用。比如有一个服务getUserInfo(String userId)。设置了Hash算法后,相同的userId的调用,都会发送到同一个 Provider。这个 Provider 上可以把用户数据在内存中进行缓存,减少访问数据库或分布式缓存的次数。如果业务上允许这部分数据有一段时间的不一致,可以考虑这种做法。减少对数据库,缓存等中间件的依赖和访问次数,同时减少了网络IO操作,提高系统性能。

负载均衡配置

如果不指定负载均衡,默认使用随机负载均衡。我们也可以根据自己的需要,显式指定一个负载均衡。 可以在多个地方类来配置负载均衡,比如 Provider 端,Consumer端,服务级别,方法级别等。

服务端服务级别

<dubbo:service interface="..." loadbalance="roundrobin" />

该服务的所有方法都使用roundrobin负载均衡。

客户端服务级别

<dubbo:reference interface="..." loadbalance="roundrobin" />

该服务的所有方法都使用roundrobin负载均衡。

服务端方法级别

<dubbo:service interface="...">
<dubbo:method name="hello" loadbalance="roundrobin"/>
</dubbo:service>

只有该服务的hello方法使用roundrobin负载均衡。

客户端方法级别

<dubbo:reference interface="...">
<dubbo:method name="hello" loadbalance="roundrobin"/>
</dubbo:reference>

只有该服务的hello方法使用roundrobin负载均衡。

和Dubbo其他的配置类似,多个配置是有覆盖关系的:

  1. 方法级优先,接口级次之,全局配置再次之。
  2. 如果级别一样,则消费方优先,提供方次之。

所以,上面4种配置的优先级是:

  1. 客户端方法级别配置。
  2. 客户端接口级别配置。
  3. 服务端方法级别配置。
  4. 服务端接口级别配置。

扩展负载均衡

Dubbo的4种负载均衡的实现,大多数情况下能满足要求。有时候,因为业务的需要,我们可能需要实现自己的负载均衡策略。本章只说明如何配置负载均衡算法。关于Dubbo扩展机制的更多内容,请前往Dubbo可扩展机制实战

  1. 实现LoadBalance接口

转自dubbo官网:

http://dubbo.apache.org/zh-cn/blog/dubbo-loadbalance.html

Dubbo的负载均衡的更多相关文章

  1. 关于dubbo的负载均衡

    1 dubbo的集群 将同一个服务部署到多个机器上,然后全部注册到注册中心.这样的多个机器就是一个dubbo集群了. 2 dubbo的负载均衡是怎么回事 由于多台机器上都有同一个服务,因此consum ...

  2. dubbo配置负载均衡、集群环境

    再用dubbo作为项目架构的时候,给consumer消费者用nginx提供了负载均衡策略和集群的实现, 但是想了下,consumer再多,但是提供者还是一个,最后还不都是落到了这一个provider上 ...

  3. Dubbo的负载均衡算法源码分析

    Dubbo提供了四种负载均衡:RandomLoadBalance,RoundRobinLoadBalance,LeastActiveLoadBalance,ConsistentHashLoadBala ...

  4. Dubbo的负载均衡策略&容错策略

    dubbo的负载均衡策略 RandomLoadBalance 随机调用负载均衡 默认方式该类实现了抽象的AbstractLoadBalance接口,重写了doSelect方法,看方法的细节就是首先遍历 ...

  5. dubbo调用负载均衡

    dubbo负载均衡的地址:http://dubbo.io/books/dubbo-user-book/demos/loadbalance.html 随机策略: public class RandomL ...

  6. dubbo之负载均衡

    在集群负载均衡时,Dubbo提供了多种均衡策略,缺省为random随机调用. Random LoadBalance 随机,按权重设置随机概率. 在一个截面上碰撞的概率高,但调用量越大分布越均匀,而且按 ...

  7. dubbo的负载均衡策略和容错策略

    一.dubbo 负载均衡策略 random loadbalance 默认情况下,dubbo 是 random load balance 随机调用实现负载均衡,可以对 provider 不同实例设置不同 ...

  8. Dubbo之负载均衡、并发控制、延迟暴露、连接控制

    1.并发控制 dubbo服务端和消费端都做了并发控制,分别在配置中有相应的对应配置: 服务端:executes服务提供者每服务每方法最大可并行执行请求数,控制并发数量:actives每服务消费者每服务 ...

  9. Dubbo动态负载均衡(socket环境实现)

    消费者 去注册中心获取信息 然后缓存到本地 如果有生产者某个服务宕机了  会通过通知的方式告知 (订阅的方式) 微服务rpc远程调用框架中,服务的负载均衡都是采用本地负载均衡的,Spring Clou ...

随机推荐

  1. 队列----java实现

    FIFO:先进先出 存储单元: public class Node { /* 元素有两部分: 元素 下一个元素的引用 */ Object data;//数据域 Node next; //指针域 pub ...

  2. Selenium Grid的Java调用方法

    java -jar selenium-server-standalone-.jar -role hub explorer http://192.168.1.173:4444/grid/console ...

  3. 一个简单的Oracle和 SQLSERVER 重建所有表索引的办法

    1. SQLSERVER 使用微软自带的存储过程 exec sp_msforeachtable 'DBCC DBREINDEX(''?'')' 2. Oracle的办法: select 'alter ...

  4. modern effective C++ -- Deducint Types

    1. 理解模板类型推导 1. expr是T& template<typename T> void f(T & param); // 我们声明如下变量 int x = 27; ...

  5. Lodop客户端本地角色注册号常见误区

    之前写过一篇关于Lodop和c-lodop注册号的区别:LODOP.C-LODOP注册号的区别第一种角色客户端本地打印角色是最常见的角色,最常见的场景,关于c-lodop云打印,它的第一种角色是取代L ...

  6. 51nod蜥蜴与地下室(1498)(暴力搜索)

    题意:一个数组s,再给你a,b值,除了s1和sn外,你可以攻击其他元素,你对这个元素的伤害为a,那么他两边的元素会受到b的牵连伤害,si-a,si-1-b,si+1-b: 求最小的次数,使得这个数组的 ...

  7. asp.net 连接SQL Server 数据库并进行相关操作

    asp.net 连接数据库,操作数据库主要需要两个类,一个是SqlConnection,一个是SqlCommand SqlConnection用于连接数据库,打开数据库,关闭数据库. 连接数据库需要特 ...

  8. BZOJ4530[Bjoi2014]大融合——LCT维护子树信息

    题目描述 小强要在N个孤立的星球上建立起一套通信系统.这套通信系统就是连接N个点的一个树. 这个树的边是一条一条添加上去的.在某个时刻,一条边的负载就是它所在的当前能够 联通的树上路过它的简单路径的数 ...

  9. poj2965 【枚举】

    The game “The Pilots Brothers: following the stripy elephant” has a quest where a player needs to op ...

  10. MT【15】证明无理数(1)

    证明:$tan3^0$是无理数. 分析:证明无理数的题目一般用反证法,最经典的就是$\sqrt{2}$是无理数的证明. 这里假设$tan3^0$是有理数,利用二倍角公式容易得到$tan6^0,tan1 ...