首页 > 数据库 > 数据库开发技术 > Dubbo服务负载均衡原理

负载均衡是rpc框架必须具备的一个能力,在Dubbo中负载均衡是在服务消费者这边实现的,也叫做客户端负载均衡。
为什么需要负载均衡机制呢?
当服务提供者集群部署的时候,服务消费方每次调用都必须选择其中一台服务提供者进行远程rpc调用。本文将分析Dubbo的负载均衡算法。

Dubbo 支持哪些负载均衡算法
1、加权随机(默认)
2、加权轮训
问题:
存在慢的提供者累积请求的问题,比如:第二台机器很慢,但没挂,当请求调到第二台时就卡在那,久而久之,所有请求都卡在调到第二台上。
3、一致性hash
特点:相同参数的请求总是发到同一提供者。当某一台提供者挂时,原本发往该提供者的请求,基于虚拟节点,平摊到其它提供者,不会引起剧烈变动。
4、加权最少活跃数
最少活跃数优先,相同活跃数的随机,活跃数指调用前后计数差(调用的慢,调用时间越长,计数差越大,因此调用的越快,活跃数越少)。使慢的提供者收到更少请求,因为越慢的提供者的调用前后计数差会越大。
源码分析
1、随机负载均衡策略
public class RandomLoadBalance extends AbstractLoadBalance {public static final String NAME = "random";/*** Select one invoker between a list using a random criteria* @param invokers List of possible invokers* @param url URL* @param invocation Invocation* @param <T>* @return The selected invoker*/protected <T> Invoker<T> doSelect(List<Invoker<T>> invokers, URL url, Invocation invocation) {// Number of invokersint length = invokers.size();// Every invoker has the same weight?boolean sameWeight = true;// the weight of every invokersint[] weights = new int[length];// the first invoker's weightint firstWeight = getWeight(invokers.get(0), invocation);weights[0] = firstWeight;// The sum of weightsint totalWeight = firstWeight;//计算每个服务提供者的权重for (int i = 1; i < length; i++) {int weight = getWeight(invokers.get(i), invocation);// save for later useweights[i] = weight;// SumtotalWeight += weight;if (sameWeight && weight != firstWeight) {sameWeight = false;}}//通过线程安全的随机生成器生成随机if (totalWeight > 0 && !sameWeight) {// 根据总权重计算一个随机权重偏移量int offset = ThreadLocalRandom.current().nextInt(totalWeight);// Return a invoker based on the random value.for (int i = 0; i < length; i++) {//遍历每个权重值,得到随机偏移量对应的权重offset -= weights[i];if (offset < 0) {return invokers.get(i);}}}// If all invokers have the same weight value or totalWeight=0, return evenly.return invokers.get(ThreadLocalRandom.current().nextInt(length));}}
总结:
加权随机是在随机的基础上,给每个服务提供者加上一个权重值。
2、加权最少活跃数
public class LeastActiveLoadBalance extends AbstractLoadBalance {public static final String NAME = "leastactive";@Overrideprotected <T> Invoker<T> doSelect(List<Invoker<T>> invokers, URL url, Invocation invocation) {// Number of invokersint length = invokers.size();// The least active value of all invokersint leastActive = -1;// The number of invokers having the same least active value (leastActive)int leastCount = 0;// The index of invokers having the same least active value (leastActive)int[] leastIndexes = new int[length];// the weight of every invokersint[] weights = new int[length];// The sum of the warmup weights of all the least active invokesint totalWeight = 0;// The weight of the first least active invokeint firstWeight = 0;// Every least active invoker has the same weight value?boolean sameWeight = true;// Filter out all the least active invokersfor (int i = 0; i < length; i++) {Invoker<T> invoker = invokers.get(i);// Get the active number of the invokeint active = RpcStatus.getStatus(invoker.getUrl(), invocation.getMethodName()).getActive();// Get the weight of the invoke configuration. The default value is 100.int afterWarmup = getWeight(invoker, invocation);// save for later useweights[i] = afterWarmup;// If it is the first invoker or the active number of the invoker is less than the current least active number//这个if 是找到活跃数最小的一个invokerif (leastActive == -1 || active < leastActive) {// Reset the active number of the current invoker to the least active numberleastActive = active;// Reset the number of least active invokers//只要找到了更小活跃数的 invoker 就重置计数leastCount = 1;// Put the first least active invoker first in leastIndexesleastIndexes[0] = i;// Reset totalWeighttotalWeight = afterWarmup;// Record the weight the first least active invokerfirstWeight = afterWarmup;// Each invoke has the same weight (only one invoker here)sameWeight = true;// If current invoker's active value equals with leaseActive, then accumulating.} else if (active == leastActive) {//这里是为了 找出并记录和最小活跃数相同的invoker// Record the index of the least active invoker in leastIndexes orderleastIndexes[leastCount++] = i;// Accumulate the total weight of the least active invoker//累计 相同活跃数invoker的 权重totalWeight += afterWarmup;// If every invoker has the same weight?if (sameWeight && i > 0&& afterWarmup != firstWeight) {sameWeight = false;}}}// Choose an invoker from all the least active invokersif (leastCount == 1) {// If we got exactly one invoker having the least active value, return this invoker directly.return invokers.get(leastIndexes[0]);}if (!sameWeight && totalWeight > 0) {// If (not every invoker has the same weight & at least one invoker's weight>0), select randomly based on// totalWeight.int offsetWeight = ThreadLocalRandom.current().nextInt(totalWeight);// Return a invoker based on the random value.for (int i = 0; i < leastCount; i++) {int leastIndex = leastIndexes[i];offsetWeight -= weights[leastIndex];if (offsetWeight < 0) {return invokers.get(leastIndex);}}}// If all invokers have the same weight value or totalWeight=0, return evenly.return invokers.get(leastIndexes[ThreadLocalRandom.current().nextInt(leastCount)]);}}
总结:
先过滤出调用次数最少(最不活跃)的调用者数量,并计算这些调用者的权重和数量。如果只有一个调用程序,则直接使用该调用程序;如果有多个调用者并且权重不相同,则根据总权重随机;如果有多个调用者且权重相同,则将其随机调用。
负载均衡机制是分布式系统中必须掌握的一项技术,通过分析Dubbo的源码实现,我们知道了常用的负载均衡算法以及一些优化版本,为学习其他中间件负载均衡机制打下基础。
原文作者:服务端技术栈;如有侵权,请联系管理员删除。