dubbo负载均衡相关策略

ConsistentHashLoadBalance

一致性hash算法,一致性hash算法简单一点说就是一个环形,然后我们的实例服务分布在这个环上,然后基于实例服务映射多个虚拟节点。一致性hash算法为什么要加入虚拟节点呢。其实就是为了防止某一个节点突然挂掉,防止这个节点的数据全部压到顺时针的第一个节点上。导致雪崩的问题。dubbo加入虚拟节点的算法

ConsistentHashSelector(List<Invoker<T>> invokers, String methodName, int identityHashCode) {
    this.virtualInvokers = new TreeMap<Long, Invoker<T>>();
    this.identityHashCode = identityHashCode;
    URL url = invokers.get(0).getUrl();
    this.replicaNumber = url.getMethodParameter(methodName, HASH_NODES, 160);
    String[] index = COMMA_SPLIT_PATTERN.split(url.getMethodParameter(methodName, HASH_ARGUMENTS, "0"));
    argumentIndex = new int[index.length];
    for (int i = 0; i < index.length; i++) {
        argumentIndex[i] = Integer.parseInt(index[i]);
    }
    // 对每个invoker走了两次for循环,然后默认是160/4,然后获取md5,然后在次hash。然后机械能添加到虚拟节点中。
    for (Invoker<T> invoker : invokers) {
        String address = invoker.getUrl().getAddress();
        for (int i = 0; i < replicaNumber / 4; i++) {
            byte[] digest = Bytes.getMD5(address + i);
            for (int h = 0; h < 4; h++) {
                long m = hash(digest, h);
                virtualInvokers.put(m, invoker);
            }
        }
    }
}
LeastActiveLoadBalance

会去尽量选择你的被调用最少的invoker,记录你的每个invoker当前活跃的调用有多少个。

@Override
protected <T> Invoker<T> doSelect(List<Invoker<T>> invokers, URL url, Invocation invocation) {
    // Number of invokers
    // 获取invker的数量
    int length = invokers.size();
    // The least active value of all invokers
    // 所有调用程序中活动最少的值
    int leastActive = -1;
    // The number of invokers having the same least active value (leastActive)
    // 具有相同最小活动值 (minimumActive) 的调用程序数
    int leastCount = 0;
    // The index of invokers having the same least active value (leastActive)
    // 具有相同最小活动值 (minimumActive) 的调用程序的索引
    int[] leastIndexes = new int[length];
    // the weight of every invokers
    int[] weights = new int[length];
    // The sum of the warmup weights of all the least active invokers
    int totalWeight = 0;
    // The weight of the first least active invoker
    int firstWeight = 0;
    // Every least active invoker has the same weight value?
    boolean sameWeight = true;


    // Filter out all the least active invokers
    for (int i = 0; i < length; i++) {
        Invoker<T> invoker = invokers.get(i);
        // Get the active number of the invoker
        // 每一次,如果你进来其以后,发现了一个invoker,他的调用此时市active。
        int active = RpcStatus.getStatus(invoker.getUrl(), invocation.getMethodName()).getActive();
        // Get the weight of the invoker's configuration. The default value is 100.
        int afterWarmup = getWeight(invoker, invocation);
        // save for later use
        weights[i] = afterWarmup;
        // If it is the first invoker or the active number of the invoker is less than the current least active number
        // 如果说发现当前invoker的活跃调用此时是比上一个invoker的活跃调用次数是小的
        // 次数会进入下面的代码
        if (leastActive == -1 || active < leastActive) {
            // Reset the active number of the current invoker to the least active number
            leastActive = active;
            // Reset the number of least active invokers
            leastCount = 1;
            // Put the first least active invoker first in leastIndexes
            leastIndexes[0] = i;
            // Reset totalWeight
            totalWeight = afterWarmup;
            // Record the weight the first least active invoker
            firstWeight = 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) {
            // Record the index of the least active invoker in leastIndexes order
            leastIndexes[leastCount++] = i;
            // Accumulate the total weight of the least active invoker
            totalWeight += afterWarmup;
            // If every invoker has the same weight?
            if (sameWeight && afterWarmup != firstWeight) {
                sameWeight = false;
            }
        }
    }
    // Choose an invoker from all the least active invokers
    if (leastCount == 1) {
    // If we got exactly one invoker having the least active value, return this invoker directly.
        // 直接就会返回当前发现的一个活跃调用次数比上一次要小的那个invoker
        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)]);
}
RandomLoadBalance

(dubbo默认的算法)随机的负载均衡。如果有权重,感觉最后亮点在最后一个for循环。

 protected <T> Invoker<T> doSelect(List<Invoker<T>> invokers, URL url, Invocation invocation) {
    // Number of invokers
    // 获取到目标服务实例集群的Invoker的数量。
    int length = invokers.size();
    // 判断这次随机算法是否要使用上权重
    if (!needWeightLoadBalance(invokers,invocation)){
        // 基于一个随机的类,nextInt,拿到invoker数量范围之内的机器对应的invoker
        return invokers.get(ThreadLocalRandom.current().nextInt(length));
    }

    // Every invoker has the same weight?
    boolean sameWeight = true;
    // the maxWeight of every invokers, the minWeight = 0 or the maxWeight of the last invoker
    int[] weights = new int[length];
    // The sum of weights
    int totalWeight = 0;
    for (int i = 0; i < length; i++) {
        // 去获取权重比例
        int weight = getWeight(invokers.get(i), invocation);
        // Sum 累加权重
        totalWeight += weight;
        // save for later use
        // 保存每次的权重,和次数的总和。
        weights[i] = totalWeight;
        if (sameWeight && totalWeight != weight * (i + 1)) {
            sameWeight = false;
        }
    }
    if (totalWeight > 0 && !sameWeight) {
        // If (not every invoker has the same weight & at least one invoker's weight>0), select randomly based on totalWeight.
        // 根据总的权重比例。然后进行随机一个数字。权重总的数据。是每个数量的相加。
        int offset = ThreadLocalRandom.current().nextInt(totalWeight);
        // Return a invoker based on the random value.
        for (int i = 0; i < length; i++) {
            // 如果offset小于了每次weights里面的数组。那么就取invokers里面的下标数据
            // 权重越大,那么权重所在的下标的值范围越大。
            if (offset < weights[i]) {
                return invokers.get(i);
            }
        }
    }
    // If all invokers have the same weight value or totalWeight=0, return evenly.
    // 如果所有调用方具有相同的权重值或 totalWeight=0,则均匀返回。
    return invokers.get(ThreadLocalRandom.current().nextInt(length));
}
RoundRobinLoadBalance

轮询算法,你有10个invokers,先调用第一个invoker,在调用第二个invoker。核心的地方selectedWRR.sel(totalWeight);这个地方把第一个调用的invoker的权限进行调整。这样就可以找到下一次最高的权重的了。

protected <T> Invoker<T> doSelect(List<Invoker<T>> invokers, URL url, Invocation invocation) {
    String key = invokers.get(0).getUrl().getServiceKey() + "." + invocation.getMethodName();
    ConcurrentMap<String, WeightedRoundRobin> map = methodWeightMap.computeIfAbsent(key, k -> new ConcurrentHashMap<>());
    int totalWeight = 0;
    long maxCurrent = Long.MIN_VALUE;
    long now = System.currentTimeMillis();
    Invoker<T> selectedInvoker = null;
    WeightedRoundRobin selectedWRR = null;
    // 进行遍历。
    for (Invoker<T> invoker : invokers) {
        String identifyString = invoker.getUrl().toIdentityString();
        // 获取权重的值。
        int weight = getWeight(invoker, invocation);
        // 再次获取判断map中是否有,没有进行添加,有久返回原来的robin
        WeightedRoundRobin weightedRoundRobin = map.computeIfAbsent(identifyString, k -> {
            WeightedRoundRobin wrr = new WeightedRoundRobin();
            wrr.setWeight(weight);
            return wrr;
        });

        if (weight != weightedRoundRobin.getWeight()) {
            //weight changed
            weightedRoundRobin.setWeight(weight);
        }
        long cur = weightedRoundRobin.increaseCurrent();
        weightedRoundRobin.setLastUpdate(now);
        // 通过这个算法,权重最高的的就是maxCurent。
        if (cur > maxCurrent) {
            maxCurrent = cur;
            selectedInvoker = invoker;
            selectedWRR = weightedRoundRobin;
        }
        totalWeight += weight;
    }
    if (invokers.size() != map.size()) {
        map.entrySet().removeIf(item -> now - item.getValue().getLastUpdate() > RECYCLE_PERIOD);
    }
    // 第一次选择的invoker。在这里就会进行权重值进行了相减的。
    // 这个就是保证下一次进行轮流的轮询。
    if (selectedInvoker != null) {
        selectedWRR.sel(totalWeight);
        return selectedInvoker;
    }
    // should not happen here
    return invokers.get(0);
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值