1. 程式人生 > >dubbo 負載均衡演算法實現的學習

dubbo 負載均衡演算法實現的學習

隨機數權重演算法

假設一個服務service1 分佈在一個含有4個節點(A, B, C, D)的叢集上。 權重分別為1,2,3,4。那麼一個請求service1 的 到 A,B,C,D 節點的概率為 10%,20%,30%,40%。 一個對於service1 的請求 會被隨機分配一個數字。這個數字是A,B,C,D 節點權重之和範圍類隨機出來的。我們的例子中權重之和是10,所以隨機數範圍是【0,9】 假設隨機出了數字 6, 那麼選擇的節點是D。原因是:6-1=5>0, 5-2=3>0, 3-3=0>=0, 0-4<0。原理是依次和各個權重相減,直到<0為止,找到D節點。

/**
 * 隨機數權重演算法
 */
public Node randomWeightAlgorithm() {
    List<Node> nodeList = new ArrayList<>();
    nodeList.add(new Node(1, 1));
    nodeList.add(new Node(2, 2));
    nodeList.add(new Node(3, 3));
    nodeList.add(new Node(4, 4));

    int len = nodeList.size();
    int totalWeight = 0;
    boolean sameWeight = true;
    for (int i = 0; i < len; i++) {
        int weight = nodeList.get(0).weight;
        totalWeight+=weight;
        if (sameWeight && i > 0 && weight != nodeList.get(i-1).weight) {
            // 節點權重都不相同
            sameWeight = false;
        }
    }
    if (totalWeight > 0 && !sameWeight) {
        int offset = ThreadLocalRandom.current().nextInt(totalWeight);
        for (Node aNodeList : nodeList) {
            offset -= aNodeList.weight;
            if (offset < 0) {
                return aNodeList;
            }
        }
    }
    // 如果節點權重相同, 則隨機返回一個節點
    return nodeList.get(ThreadLocalRandom.current().nextInt(len));
}

基於權重的輪詢演算法

普通的輪詢就是權重相同的情況。如果服務service1 部署在叢集A,B,C,D四臺機器上。那麼第一個服務請求被路由到機器A,第二個請求被路由到機器B, 依次類推。 當4個節點許可權不同時,維護一個map<服務標記位,權重計數器>。 每次請求過來,權重計數器+1。 每次請求過來,找出出最大,最小的權重。(權重計數器+1) % 最大權重, 得到這次請求的權重基準線。找出>權重基準線的節點們,再用當前許可權基準線 % 這些節點的數量(Size) = 要選擇的節點的下標。就可以獲得選擇的節點。

// <唯一標識介面的id, 權重計數器>
private final ConcurrentMap<Integer, AtomicPositiveInteger> sequences = new ConcurrentHashMap<>();

  /**
 * 基於權重的輪訓排程演算法
 */
public Node roundRobinLoadBalance() {
    // mock 4 個 節點
    List<Node> nodeList = new ArrayList<>();
    nodeList.add(new Node(1, 1));
    nodeList.add(new Node(2, 2));
    nodeList.add(new Node(3, 3));
    nodeList.add(new Node(4, 4));
    // 假設一個id = 5 標識一個唯一的介面
    int key = 5;
    int length = nodeList.size();
    int maxWeight = 0;
    int minWeight = Integer.MAX_VALUE;
    // 找出最大,最小權重值
    for (int i = 0; i< length; i++) {
        int weight = nodeList.get(i).weight;
        maxWeight = Math.max(maxWeight, weight);
        minWeight = Math.min(minWeight, weight);
    }
    // 4個節點的權重不同
    if (maxWeight > 0 && minWeight < maxWeight) {
        AtomicPositiveInteger weightSequence = sequences.get(key);
        if (weightSequence == null) {
            sequences.putIfAbsent(key, new AtomicPositiveInteger());
        }
        int currentWeight = weightSequence.getAndIncrement() % maxWeight;
        List<Node> nodesAfterSelect = new CopyOnWriteArrayList<>();
        // 篩選權重>當前權重的節點們
        for (Node node : nodeList) {
            if (node.weight > currentWeight) {
                nodesAfterSelect.add(node);
            }
        }
        int weightLength = nodesAfterSelect.size();
        if (weightLength == 1) {
            return nodesAfterSelect.get(0);
        }
        if (weightLength > 1) {
            return nodesAfterSelect.get(currentWeight % weightLength);
        }
    }
    // 四個節點權重相同
    AtomicPositiveInteger sequence =  sequences.get(key);
    if (sequence == null) {
        sequences.putIfAbsent(key, new AtomicPositiveInteger());
        sequence = sequences.get(key);
    }
    // 通過取模來輪訓某個節點
    return nodeList.get(sequence.getAndIncrement() % length);
}