Dubbo篇:负载均衡策略源码分析



概述



       在进行消费端服务调用的时候,看到初始化了LoadBalance,通过负载均衡获取一个可用的节点。LoadBalance也是一个扩展点,Dubbo内置了4种负载均衡算法,
都继承自AbstractLoadBalance,AbstractLoadBalance中实现通用逻辑,留一个抽象方法doSelect方法给子类实现,默认是RandomLoadBalance实现。四个负载均衡实现子类如下:

             RandomLoadBalance:随机,按权重设置随机概率。在一个截面上碰撞的概率高,但调用量越大分布越均匀,而且按概率使用权重后也比较均匀,有利于动态调整提供者的权重。

             RoundRobinLoadBalance:轮询,按公约后的权重设置轮询比例。存在慢的提供者累计请求的问题,比如:第二天机器很慢,但没“挂”,当请求调到第二台时就卡在那里,久而久之,所以请求都卡在调到第二台上。

             LeastActiveLoadBalance:最少活跃调用数,如果活跃数相同则随机调用,活跃数指调用前后计数差。使慢的提供者收到更少请求,因为越慢的提供者的调用前后计数差会越大。

             ConsistentHashLoadBalance:一致性Hash,相同参数的请求总是发到同一提供者。当某一台提供者“挂”是,原本发往该提供者的请求,基于虚拟节点,会平摊到其他提供者,不会引起剧烈变动。默认只对第一个参数“Hash”,默认使用160份虚拟节点。



AbstractLoadBalance



       AbstractLoadBalance实现较为简单,提供了一个select方法调用子类重写的doSelect方法,提供了getWeight用于计算权重,代码实现如下:

Dubbo篇:负载均衡策略源码分析_第1张图片
在这里插入图片描述

       getWeight获取当前权重,其中调用了calculateWarmupWeight,主要实现了获取权重的时的预热,服务启动是并不会直接给予100%的流量,计算逻辑是:(启动至今时间 / 给予的预热总时间)* 权重。



Random负载均衡



       RandomLoadBalance按照权重设置随机概率做负载均衡,这种算法并不能精确地做平均请求,但是随着请求数量的增加,最终结果大致平均,代码实现如下:

Dubbo篇:负载均衡策略源码分析_第2张图片
在这里插入图片描述

       时间比较简单,如果权重都一样,则直接随机返回一个,如果权重不一样,则把每个Invoker的权重看作一个区间,以总权重为基础获取一个随机数,然后用随机数依次减去每个权重,当随机数小于零的时候结束,即看这个随机数落在哪个invoker的权重区间中,就返回这个invoker。



RoundRobin负载均衡



       RoundRobin负载均衡算法的实现为平滑权重轮询算法。在轮询的基础上加上了负载均衡的算法。实现代码如下:

Dubbo篇:负载均衡策略源码分析_第3张图片
在这里插入图片描述

       算法逻辑是每次请求做负载均衡时,会遍历所有Invoker节点,对于每个Invoker,会让它的current = current + weight。同时会把每个Invoker的weight累加到totalWeight总权重,即totalWeight = totalWeight + weight。遍历完成后会保存current值最大的就是本次要返回的节点,返回前会将其current减去totalWeight,即current = current - totalWeight,现在选择的时候会在现有的current值上基础上再做上次操作,将totalWeight置0,重复上述逻辑。选中过的Invoker的current值会因为减去了totalWeight,所以最小,再次选中它的话就要等根据自身权重一次一次加成最大,因此权重大的就更快的再次被选中,权重小的就慢些被再次被选中,而且不也会一直调用权重大的Invoker,会有权重小的穿插进去。



LeastActive负载均衡



       LeastActive负载均衡是最小活跃调用数负载均衡,框架会记下每个Invoker的活跃数,每次只从活跃数最小的Invoker里选一个节点。这个负载均衡算法需要配合ActiveLimitFilter过滤器来计算每个接口方法的活跃数。LeastActiveLoadBalance代码实现如下:

Dubbo篇:负载均衡策略源码分析_第4张图片
在这里插入图片描述

       其实现逻辑较为简单,遍历invoker列表,找出其所有活跃数最小的invoker,保存到一个集合中,然后用Random负载均衡方式从中选出一个Invoker。



一致性Hash负载均衡



       一致性Hash负载均衡可以让参数相同的请求每次都路由到相同的机器上。这种负载均衡的方式可以然后请求相对平均,当某些节点下线时,请求会平摊到其他服务者,不会引起剧烈的变动。Dubbo框架使用了优化过Ketama一致性Hash,这种算法会为每个节点在创建多个虚拟节点,让节点在环形上的分布更均匀,后续的调用也会随之更加均匀。ConsistentHashLoadBalance代码实现如下:

Dubbo篇:负载均衡策略源码分析_第5张图片
在这里插入图片描述

       逻辑核心在ConsistentHashSelector中,ConsistentHashSelector初始化时会对节点进行散列,散列的环形使用一个TreeMap实现,所有的真实、虚拟的节点都会放入TreeMap。把节点的IP+递增数字做“MD5”,以此作为节点标识,再对标识做hash得到TreeMap的key,最后把可以调用的节点作为TreeMap的value。在客户端调用的时候,只要对请求的参数也做MD5即可,虽然此时得到的MD5之不一定能对应到TreeMap的key,因为每次请求的参数不一样,以为TreeMap是有序树形结构,可以调用TreeMap的ceilingEntry方法,用于返回一个至少大于或等于当前给定key的Entry,从而达到顺时针往前找的效果。如果找不到,则使用firstEntry返回第一个节点。



参考:

《深入理解 Apache Dubbo与实战》

你可能感兴趣的:(Dubbo篇:负载均衡策略源码分析)