LoadBalance
Dubbo中有四种LB的方式:随机、轮询、最少活跃和一致哈希
接口LoadBalance 的定义说明,LoadBalance 的实现只是在一个服务提供的调用者列表(invokers)中选出一个调用者即可,默认的负载方式是随机负载均衡(@SPI(RandomLoadBalance.NAME)),我们也可以指定使用哪种负载均衡:
代码语言:javascript复制<dubbo:reference interface="xxx" loadbalance="roundrobin"/> 或 <dubbo:service interface="xxx" loadbalance="roundrobin" />
各种负载均衡的实现方式
随机负载均衡(RandomLoadBalance):
先统计所有提供者上该接口方法的权重总和,然后对这个总和随机nextInt一下,看生成的随机数落到哪个段内,就调哪个提供者上的该服务。
轮询负载均衡(RoundRobinLoadBalance):
如果该接口方法的所有提供者的权重一样,则直接内部的序列计数器(sequences) 1然后对提供者的数量进行取模来决定调用哪个提供者上的服务
如果该接口方法的所有提供者的权重不一样,则找到其中最大的权重,然后将内部的权重计数器(weightSequences) 1并对该最大权重数取模,得到一个权重基数,然后再找出权重比权重基数大的提供者列表,最后通过内部的序列计数器(sequences) 1然后对这个提供者列表的数量进行取模,来轮询。
当提供者的权重不一样时,之所以使用权重基数的方式,是因为基数权重是会变的,它等于weightSequences这个原子类 对 最大权重取模的结果,并且每次都会进行自增。因此,权重基数会先变大后变小。在大于权重基数的提供者列表中进行轮询,那么权重大的提供者自然得到更多的轮询机会。
最少活跃负载均衡(LeastActiveLoadBalance):
每个接口和接口方法都对应一个RpcStatus对象,记录了他们的活跃数、失败数等等相关统计信息。
此种负载均衡方式:
- 筛选出活跃数最低的提供者列表A,如果只有1个那就直接返回了
- 如果提供者列表A的所有提供者权重一样,那就随机选一个返回。
- 权重不一样,则计算出总权重,然后算出随机值,根据随机值在总权重哪一个位置,就返回对应的提供者。
活跃数就像并发量降级中的计数器一样,开始调用时活跃数 1,调用结束时活跃数-1,所以活跃值越大,表明该提供者提供者的该接口方法耗时越长,而消费能力强的提供者接口往往活跃值很低。最少活跃负载均衡保证了“慢”提供者能接收到更少的提供者调用。
一致哈希负载均衡(ConsistentHashLoadBalance):
一致性哈希算法的负载均衡保证了同样的请求(参数)将会落到同一台提供者上,这在某些场景是非常有用的,Dubbo中默认采用了160个虚拟节点,因为Dubbo的请求URL中除了我们使用的参数,还有些额外的系统调用参数,比如timestamp、loadbalance、pid和application等,有人可定会问,Dubbo会对URL中哪些参数进行hash,Dubbo默认除了对我们接口所有参数进行hash外,还会加上这些额外参数,因为有timestamp,这是不是也意味着在Dubbo的重试调用时timestamp不变?
其实现方式就是:生成虚拟节点,使用TreeMap保存,然后获取第一个节点进行调用
总结
上述的四种负载均衡,除了一致性哈希,其他三种都依赖了接口方法的权重统计,借助权重的不同,随机负载均衡就能做到动态调整的效果,Dubbo中的轮询负载方式,也利用了权重,那么有人会问,Dubbo中的随机和轮询是不是差别不大?是的,笔者也这样认为,相似的效果,也有着相似的缺点,Dubbo中的随机和轮询负载都没有考虑到提供者提供者消费服务的能力,如果相差很大,“慢”提供者有可能被“快”提供给者给拖垮,其根本原因也是这两种负载均衡的加权因子考虑的不是服务耗时。最少活跃的负载均衡就很巧妙的解决了此问题,而且它不是直接通过统计服务调用的耗时,而是采用统计调用差(活跃数)。一致性哈希特别适用于有缓存的系统,这样缓存命中率会比较高。
参考
一致性hash负载均衡