dubbo 負載均衡演算法實現的學習
阿新 • • 發佈:2018-12-18
隨機數權重演算法
假設一個服務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); }