Dubbo 睡前小故事系列之RandomLoadBalance


RandomLoadBalance 是Dubbo的负载均衡提供的策略之一。

如何设计一个随机算法

答案很多~假如在这样一个场景,A、B、C、D四个物品权重分别为1、2、3、4,如何在考虑权重的情况下,设计一个随机获取某个物品的算法。有如下的方式:

  • 对四个物品做一次遍历,按序进行累加,用线段可表示如下,线段越长表示落入该区域的概率越大。
  • 然后随机获取一个1到10之间的整数,线段越长,表示落在这个范围的整数概率越搞,即可在考虑权重的情况下,达到随机的目的。

在这里插入图片描述

RandomLoadBalance

明白上诉原理,RandomLoadBalance就不过如此了:

  • 在进行第一次遍历时顺带判断权重是否一致,一致的话随机获取一个即可。
 @Override
    protected <T> Invoker<T> doSelect(List<Invoker<T>> invokers, URL url, Invocation invocation) {
        // Number of invokers
        int length = invokers.size();
        // 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++) {
                if (offset < weights[i]) {
                    // 返回第一个大于权重的invoker
                    return invokers.get(i);
                }
            }
        }
        // 如果所有invoker都一致, 直接返回一个临近随机权重的invoker
        // If all invokers have the same weight value or totalWeight=0, return evenly.
        return invokers.get(ThreadLocalRandom.current().nextInt(length));
    }
  • 关于ThreadLocalRandom,它是JDK 1.7出现的一个获取随机数的类,根据其解释,可以解决并发下其他随机函数竞争统一资源时产生多余的消耗,咋样的消耗?

关于ThreadLocalRandom

先看Random#nextInt方法下的具体实现,可以看到每次获取随机数,都是依赖随机种子的,那么在并发的情况下,由于多个线程访问同一个随机种子,就有可能导致计算出来的结果是一样的。

咋办,可以看到代码里加了CAS,但又引入一个问题,在争抢资源中失败的线程就进入CPU空转了,降低性能。

protected int next(int bits) {
        long oldseed, nextseed;
        AtomicLong seed = this.seed;
        do {
            oldseed = seed.get();
            nextseed = (oldseed * multiplier + addend) & mask;
        } while (!seed.compareAndSet(oldseed, nextseed));
        return (int)(nextseed >>> (48 - bits));
    }

ThreadLocalRandom使用方式:

 ThreadLocalRandom.current().nextInt(totalWeight);

在使用时先通过current方法,为每个第一次调用该方法的线程分配了SEED

public static ThreadLocalRandom current() {
        if (UNSAFE.getInt(Thread.currentThread(), PROBE) == 0)
            localInit();
        return instance;
    }
 /**
     * Initialize Thread fields for the current thread.  Called only
     * when Thread.threadLocalRandomProbe is zero, indicating that a
     * thread local seed value needs to be generated. Note that even
     * though the initialization is purely thread-local, we need to
     * rely on (static) atomic generators to initialize the values.
     */
    static final void localInit() {
        int p = probeGenerator.addAndGet(PROBE_INCREMENT);
        int probe = (p == 0) ? 1 : p; // skip 0
        long seed = mix64(seeder.getAndAdd(SEEDER_INCREMENT));
        Thread t = Thread.currentThread();
        UNSAFE.putLong(t, SEED, seed);
        UNSAFE.putInt(t, PROBE, probe);
    }

呃呃,对了Dubbo的几个版本对RandomLoadBalance的可能不太一样,但原理大径相同。上面是现在master分支的实现方式。(截止至2021年5月22日 02:15)

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

legendaryhaha

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

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

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

打赏作者

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

抵扣说明:

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

余额充值