Soul网关源码探秘《十》 - 负载均衡 - Hash/RoundRobin

前文分析了DividePlugin插件中负载均衡的总体结构以及Random的具体实现。今天来探索剩下两种负载均衡是如何实现的。

准备工作

Soul网关源码探秘《八》 - 负载均衡初探

Hash

找到具体实现类HashLoadBalance直接查看

public DivideUpstream doSelect(final List<DivideUpstream> upstreamList, final String ip) {
        // 1. 遍历服务列表,为每个服务在生成 VIRTUAL_NODE_NUM 条(默认为5条)的 hash-服务 记录,放入一个有序 treemap 存放
        for (DivideUpstream address : upstreamList) {
            for (int i = 0; i < VIRTUAL_NODE_NUM; i++) {
                long addressHash = hash("SOUL-" + address.getUpstreamUrl() + "-HASH-" + i);
                treeMap.put(addressHash, address);
            }
        }
        // 2. 使用参数 ip 生成一个 hash 值
        long hash = hash(String.valueOf(ip));
        // 3. 在 treemap 中找出 key 比 hash 大的所有记录
        SortedMap<Long, DivideUpstream> lastRing = treeMap.tailMap(hash);
        if (!lastRing.isEmpty()) {
        // 4. 返回符合条件的第一条记录
            return lastRing.get(lastRing.firstKey());
        }
        // 5. 如果没有 key 比 hash 大的记录,则返回 treemap 中的第一条记录
        return treeMap.firstEntry().getValue();
    }

可以看到,在 hash 算法中,并没有使用到权重值参与到计算中。

看完还有以下两个问题有点懵:

  1. 没有看懂这样实现负载均衡有啥优势?
  2. 怎么保证转发更加平均?

RoundRobin

找到RoundRobinLoadBalance查看具体实现:

public DivideUpstream doSelect(final List<DivideUpstream> upstreamList, final String ip) {
        // ...
        // 1. 每次接收到请求的总权重
        int totalWeight = 0;
        long maxCurrent = Long.MIN_VALUE;
        // ...
        for (DivideUpstream upstream : upstreamList) {
            // ...
            // 2. 每一个服务中维护一个积累值,积累值每次增加的值为该服务的权重值
            long cur = weightedRoundRobin.increaseCurrent();
            // ...
            // 3. 如果当前服务积累值大于 maxCurrent,则选择该服务;继续下一次循环
            if (cur > maxCurrent) {
                maxCurrent = cur;
                selectedInvoker = upstream;
                selectedWRR = weightedRoundRobin;
            }
            // 4. 总权重增加
            totalWeight += weight;
        }
        // 5. 没搞懂这一部分是会在什么情况下触发;以及有什么用处?
        if (!updateLock.get() && upstreamList.size() != map.size() && updateLock.compareAndSet(false, true)) {
            try {
                // copy -> modify -> update reference
                ConcurrentMap<String, WeightedRoundRobin> newMap = new ConcurrentHashMap<>(map);
                newMap.entrySet().removeIf(item -> now - item.getValue().getLastUpdate() > recyclePeriod);
                methodWeightMap.put(key, newMap);
            } finally {
                updateLock.set(false);
            }
        }
        if (selectedInvoker != null) {
          // 6. 如果选中某个服务,那么这个服务对应的 cur 减去总权重进入负值阶段,重新开始积累怒气值
         selectedWRR.sel(totalWeight);
            return selectedInvoker;
        }
        // should not happen here
        return upstreamList.get(0);
    }

可以看到对应于每一个服务会维护一个该服务的怒气值,RoundRobin算法会经历这两个阶段:

  1. 每当接收到请求后,每一个服务都会增加相当于自身权重值的怒气值。
  2. 最终怒气值最高的服务在这一轮胜出,执行转发请求的任务。同时,该服务的怒气值会减去总权重进入负值阶段重新开始积攒怒气值。

例如,现有权重分别为50,100的两个服务。

第一轮的 cur 依次为 50,100。100权重的服务胜出并将 cur 减去总权重。最终得到 cur 为 50,-50(100-150)
第二轮的 cur 依次为 100,50(-50+100)。50权重的服务胜出并将 cur 减去总权重。最终得到 cur 为 -50(100-150),50
第二轮的 cur 依次为 0,150。100权重的服务胜出并将 cur 减去总权重。最终得到 cur 为 0,0(150-150)。如此往复。。。


总结

Random 是每次接收请求都是独立按照各自权重进行计算;
而 RoundRobin 是把过往的每一次都算数。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

rughru

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值