DUBBO的最小活跃数算法

DUBBO的最小活跃数算法

  • 最小活跃数

参考链接: 2 万字长文带你细细盘点五种负载均衡策略

最小活跃数

  最小活跃数负载均衡:需要配合 activeFilter 使用,活跃数在方法调用前后进行维护,响应越快的服务器堆积的请求越少,对应的活跃数也少。Dubbo 在选择的时候遵循下面的规则,有最小活跃数用最小活跃数,没有最小活跃数根据权重选择,权重一样则随机返回的负载均衡算法。
  在LeastActiveLoadBalance类中,唯一的方法是doSelect方法,接下来看一下doSelect方法。前面是一些变量的初始化,紧接着下面一个for循环遍历所有invokers(即服务结点),选取活跃数最小的结点,并存储对应权值,如果有存在多个相同最小活跃数的结点,则都存储起来。选取之后,如果只有一个最小活跃数的结点,那么返回对应的服务器结点即可;如果有多个相同最小活跃数的结点,只要活跃值不都是0,就根据权重随机选择一个结点,具体是从总权重中选取一个权重,遍历结点,减去结点的权重,直到这个权重小于0;如果权重都相同,那么随机返回一个结点。

@Override
protected <T> Invoker<T> doSelect(List<Invoker<T>> invokers, URL url, Invocation invocation) {
    // Number of invokers
    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)
    int leastCount = 0;
    // The index of invokers having the same least active value (leastActive)
    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
        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
        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.
        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)]);
}

注意活跃数是来自于RpcStatus的,beaginCount方法会增加活跃数,endCount方法会减少活跃数。

public static RpcStatus getStatus(URL url, String methodName) {
    String uri = url.toIdentityString();
    ConcurrentMap<String, RpcStatus> map = (ConcurrentMap)METHOD_STATISTICS.computeIfAbsent(uri, (k) -> {
        return new ConcurrentHashMap();
    });
    return (RpcStatus)map.computeIfAbsent(methodName, (k) -> {
        return new RpcStatus();
    });
}
public static void beginCount(URL url, String methodName) {
    beginCount(url, methodName, 2147483647);
}
public static boolean beginCount(URL url, String methodName, int max) {
    max = max <= 0 ? 2147483647 : max;
    RpcStatus appStatus = getStatus(url);
    RpcStatus methodStatus = getStatus(url, methodName);
    if (methodStatus.active.get() == 2147483647) {
        return false;
    } else {
        int i;
        do {
            i = methodStatus.active.get();
            if (i == 2147483647 || i + 1 > max) {
                return false;
            }
        } while(!methodStatus.active.compareAndSet(i, i + 1));

        appStatus.active.incrementAndGet();
        return true;
    }
}
public static void endCount(URL url, String methodName, long elapsed, boolean succeeded) {
    endCount(getStatus(url), elapsed, succeeded);
    endCount(getStatus(url, methodName), elapsed, succeeded);
}
private static void endCount(RpcStatus status, long elapsed, boolean succeeded) {
    status.active.decrementAndGet();
    status.total.incrementAndGet();
    status.totalElapsed.addAndGet(elapsed);
    if (status.maxElapsed.get() < elapsed) {
        status.maxElapsed.set(elapsed);
    }

    if (succeeded) {
        if (status.succeededMaxElapsed.get() < elapsed) {
            status.succeededMaxElapsed.set(elapsed);
        }
    } else {
        status.failed.incrementAndGet();
        status.failedElapsed.addAndGet(elapsed);
        if (status.failedMaxElapsed.get() < elapsed) {
            status.failedMaxElapsed.set(elapsed);
        }
    }
}

你可能感兴趣的:(RPC,算法题,dubbo,算法,java)