Java負(fù)載均衡算法實(shí)現(xiàn)之輪詢和加權(quán)輪詢
1.普通輪詢算法
輪詢(Round Robin,RR)是依次將用戶的訪問請求,按循環(huán)順序分配到web服務(wù)節(jié)點(diǎn)上,從1開始到最后一臺服務(wù)器節(jié)點(diǎn)結(jié)束,然后再開始新一輪的循環(huán)。這種算法簡單,但是沒有考慮到每臺節(jié)點(diǎn)服務(wù)器的具體性能,請求分發(fā)往往不均衡。
代碼實(shí)現(xiàn):
/** * 普通輪詢算法 */public class RoundRobin { private static Integer index = 0; private static List<String> nodes = new ArrayList<>(); // 準(zhǔn)備模擬數(shù)據(jù) static { nodes.add("192.168.1.101"); nodes.add("192.168.1.103"); nodes.add("192.168.1.102"); System.out.println("普通輪詢算法的所有節(jié)點(diǎn):"+nodes);//打印所有節(jié)點(diǎn) } // 關(guān)鍵代碼 public String selectNode(){ String ip = null; synchronized (index){ // 下標(biāo)復(fù)位 if(index>=nodes.size()) index = 0; ip = nodes.get(index); index++; } return ip; } // 并發(fā)測試:兩個(gè)線程循環(huán)獲取節(jié)點(diǎn) public static void main(String[] args) { new Thread(() -> { RoundRobin roundRobin1 = new RoundRobin(); for (int i=1;i<=5;i++){ String serverIp = roundRobin1.selectNode(); System.out.println(Thread.currentThread().getName()+"==第"+i+"次獲取節(jié)點(diǎn):"+serverIp); } }).start(); RoundRobin roundRobin2 = new RoundRobin(); for (int i=1;i<=nodes.size();i++){ String serverIp = roundRobin2.selectNode(); System.out.println(Thread.currentThread().getName()+"==第"+i+"次獲取節(jié)點(diǎn):"+serverIp); } } }
執(zhí)行結(jié)果:不同線程訪問,結(jié)果依舊是按順序循環(huán)分配節(jié)點(diǎn)
普通輪詢算法的所有節(jié)點(diǎn):[192.168.1.101, 192.168.1.103, 192.168.1.102]
main==第1次獲取節(jié)點(diǎn):192.168.1.101
Thread-0==第1次獲取節(jié)點(diǎn):192.168.1.103
Thread-0==第2次獲取節(jié)點(diǎn):192.168.1.102
Thread-0==第3次獲取節(jié)點(diǎn):192.168.1.101
Thread-0==第4次獲取節(jié)點(diǎn):192.168.1.103
Thread-0==第5次獲取節(jié)點(diǎn):192.168.1.102
main==第2次獲取節(jié)點(diǎn):192.168.1.101
main==第3次獲取節(jié)點(diǎn):192.168.1.103
2.加權(quán)輪詢算法
加權(quán)輪詢(Weighted Round Robin,WRR)是根據(jù)設(shè)定的權(quán)重值來分配訪問請求,權(quán)重值越大的,被分到的請求數(shù)也就越多。一般根據(jù)每臺節(jié)點(diǎn)服務(wù)器的具體性能來分配權(quán)重。
2.1.實(shí)現(xiàn)方式一
將需要輪詢的所有節(jié)點(diǎn)按權(quán)重?cái)?shù)循環(huán)生成一個(gè)List 集合,然后就跟普通輪詢算法一樣,來一個(gè)、分配一個(gè)、進(jìn)1位。
例如:
所有節(jié)點(diǎn)信息:{{“192.168.1.100“,5},{“192.168.1.101“,1},{“192.168.1.102“,3}}
那么生成的List 集合為:
{“192.168.1.100“,
“192.168.1.100“,
“192.168.1.100“,
“192.168.1.100“,
“192.168.1.100“,
“192.168.1.101“,
“192.168.1.102“,
“192.168.1.102“,
“192.168.1.102“}
后面就是普通輪詢算法的邏輯
代碼實(shí)現(xiàn):
類似于二維數(shù)組 降維成 一維數(shù)組,然后使用普通輪詢
/** * 簡單版的加權(quán)輪詢 */public class WeightedRoundRobinSimple { private static Integer index = 0; private static Map<String,Integer> mapNodes = new HashMap<>(); // 準(zhǔn)備模擬數(shù)據(jù) static { mapNodes.put("192.168.1.101",1); mapNodes.put("192.168.1.102",3); mapNodes.put("192.168.1.103",2); /* -- 以下代碼只為了方便查看所有節(jié)點(diǎn),刪除不影響 -- S */ List<String> nodes = new ArrayList<>(); Iterator<Map.Entry<String, Integer>> iterator = mapNodes.entrySet().iterator(); while (iterator.hasNext()){ Map.Entry<String, Integer> entry = iterator.next(); String key = entry.getKey(); for (int i=0;i<entry.getValue();i++){ nodes.add(key); } } System.out.println("簡單版的加權(quán)輪詢:"+nodes);//打印所有節(jié)點(diǎn) /* -- 以上代碼只為了方便查看所有節(jié)點(diǎn),刪除不影響-- E */ } // 關(guān)鍵代碼:類似于二維數(shù)組 降維成 一維數(shù)組,然后使用普通輪詢 public String selectNode(){ List<String> nodes = new ArrayList<>(); Iterator<Map.Entry<String, Integer>> iterator = mapNodes.entrySet().iterator(); while (iterator.hasNext()){ Map.Entry<String, Integer> entry = iterator.next(); String key = entry.getKey(); for (int i=0;i<entry.getValue();i++){ nodes.add(key); } } String ip = null; synchronized (index){ // 下標(biāo)復(fù)位 if(index>=nodes.size()) index = 0; ip = nodes.get(index); index++; } return ip; } // 并發(fā)測試:兩個(gè)線程循環(huán)獲取節(jié)點(diǎn) public static void main(String[] args) { new Thread(() -> { WeightedRoundRobinSimple roundRobin1 = new WeightedRoundRobinSimple(); for (int i=1;i<=6;i++){ String serverIp = roundRobin1.selectNode(); System.out.println(Thread.currentThread().getName()+"==第"+i+"次獲取節(jié)點(diǎn):"+serverIp); } }).start(); WeightedRoundRobinSimple roundRobin2 = new WeightedRoundRobinSimple(); for (int i=1;i<=6;i++){ String serverIp = roundRobin2.selectNode(); System.out.println(Thread.currentThread().getName()+"==第"+i+"次獲取節(jié)點(diǎn):"+serverIp); } } }
執(zhí)行結(jié)果:兩個(gè)線程循環(huán)測試,輸出結(jié)果會出現(xiàn)交替分配到不同的IP,但最終的效果都是一個(gè)個(gè)按順序分配,類似于普通輪詢算法。
簡單版的加權(quán)輪詢:[192.168.1.103, 192.168.1.103, 192.168.1.101, 192.168.1.102, 192.168.1.102, 192.168.1.102]
main==第1次獲取節(jié)點(diǎn):192.168.1.103
main==第2次獲取節(jié)點(diǎn):192.168.1.103
main==第3次獲取節(jié)點(diǎn):192.168.1.101
main==第4次獲取節(jié)點(diǎn):192.168.1.102
main==第5次獲取節(jié)點(diǎn):192.168.1.102
Thread-0==第1次獲取節(jié)點(diǎn):192.168.1.102
Thread-0==第2次獲取節(jié)點(diǎn):192.168.1.103
main==第6次獲取節(jié)點(diǎn):192.168.1.103
Thread-0==第3次獲取節(jié)點(diǎn):192.168.1.101
Thread-0==第4次獲取節(jié)點(diǎn):192.168.1.102
Thread-0==第5次獲取節(jié)點(diǎn):192.168.1.102
Thread-0==第6次獲取節(jié)點(diǎn):192.168.1.102
2.2.實(shí)現(xiàn)方式二(重點(diǎn)難點(diǎn))
本文的重點(diǎn)難點(diǎn)。
在實(shí)現(xiàn)方式一的算法中可以很明顯的看到,同權(quán)重的IP會被連續(xù)分配,也就是說同一個(gè)IP在短時(shí)間內(nèi)收到不同的請求,過了這個(gè)連續(xù)點(diǎn),就要等到下一輪才會被分配到,并沒有做到均勻分配節(jié)點(diǎn)。
實(shí)現(xiàn)方式二將盡可能地均勻分配每個(gè)節(jié)點(diǎn),節(jié)點(diǎn)分配不再是連續(xù)的,但最終的權(quán)重比和上一個(gè)方式一樣,這種加權(quán)輪詢又被稱為平滑加權(quán)輪詢。
理解關(guān)鍵的幾個(gè)參數(shù)和算法邏輯,方便理解代碼的實(shí)現(xiàn)。
2.2.1.概述
關(guān)鍵參數(shù)
ip:負(fù)載IP
weight:權(quán)重,保存配置的權(quán)重
effectiveWeight:有效權(quán)重,輪詢的過程權(quán)重可能變化
currentWeight:當(dāng)前權(quán)重,比對該值大小獲取節(jié)點(diǎn)
注意幾個(gè)點(diǎn):
weight 權(quán)重,在整個(gè)過程不會對它做修改,只用來保存配置時(shí)的權(quán)重參數(shù)值。如果直接拿weight 運(yùn)算而不保存配置的最原始權(quán)重參數(shù),那么將會丟失最關(guān)鍵的用戶配置的權(quán)重參數(shù)。
effectiveWeight 有效權(quán)重,在整個(gè)過程可能會變化,初始值等于weight,主要用于當(dāng)節(jié)點(diǎn)出現(xiàn)分配失敗時(shí)降低權(quán)重值,成功時(shí)提高權(quán)重值(但不能大于weight值),本案例為了簡化算法,并未加入這功能,因此本案例中effectiveWeight始終等于weight。
currentWeight 當(dāng)前權(quán)重,通過循環(huán)所有節(jié)點(diǎn)比對該值大小來分配權(quán)重最大的節(jié)點(diǎn),初始值等于weight。
三個(gè)權(quán)重參數(shù)的變化情況
僅僅針對本案例,因?yàn)楸景咐秊榱撕喕惴?,并未加入[節(jié)點(diǎn)出現(xiàn)分配失敗時(shí)降低權(quán)重值,成功時(shí)提高權(quán)重值(但不能大于weight值)的功能],所以有效權(quán)重effectiveWeight 不會發(fā)生變化。
第一次加權(quán)輪詢時(shí):currentWeight = weight = effectiveWeight;
后面每次加權(quán)輪詢時(shí):currentWeight 的值都會不斷變化,weight 和effectiveWeight 的值不變;
被分配的節(jié)點(diǎn)的currentWeight = currentWeight - 權(quán)重之和
所有節(jié)點(diǎn)的currentWeight = currentWeight + effectiveWeight
2.2.2.舉個(gè)例子理解算法
你面前有三個(gè)瓶子A、B、C,分別裝有1L、3L、2L水。
第一輪分配情況:B多,所以把B瓶子的3L水,分1L給A,分2L給C(按權(quán)重分),分完之后:A、B、C分別為:2L、0L、4L
第二輪分配情況:C多,所以把C瓶子的4L水,分1L給A,分3L給B(按權(quán)重分),分完之后:A、B、C分別為:3L、3L、0L
第三輪分配情況:A和B一樣多,那么拿誰去分呢?拿誰其實(shí)都一樣(算法中寫了A大于B才選A,現(xiàn)在等于,所以不選A),所以把B瓶子的3L水,分1L給A,分2L給C(按權(quán)重分),分完之后:A、B、C分別為:4L、0L、2L
然后不斷的進(jìn)行下去……
簡化成數(shù)學(xué)邏輯(代碼實(shí)現(xiàn))的關(guān)鍵兩步
被分配的節(jié)點(diǎn)的currentWeight = currentWeight - 權(quán)重之和
所有節(jié)點(diǎn)的currentWeight = currentWeight + effectiveWeight
下面通過閱讀代碼來理解
2.2.3.代碼實(shí)現(xiàn)
節(jié)點(diǎn)對象
/** * String ip:負(fù)載IP * final Integer weight:權(quán)重,保存配置的權(quán)重 * Integer effectiveWeight:有效權(quán)重,輪詢的過程權(quán)重可能變化 * Integer currentWeight:當(dāng)前權(quán)重,比對該值大小獲取節(jié)點(diǎn) * 第一次加權(quán)輪詢時(shí):currentWeight = weight = effectiveWeight * 后面每次加權(quán)輪詢時(shí):currentWeight 的值都會不斷變化,其他權(quán)重不變 */public class Node implements Comparable<Node>{ private String ip; private final Integer weight; private Integer effectiveWeight; private Integer currentWeight; public Node(String ip,Integer weight){ this.ip = ip; this.weight = weight; this.effectiveWeight = weight; this.currentWeight = weight; } public Node(String ip, Integer weight, Integer effectiveWeight, Integer currentWeight) { this.ip = ip; this.weight = weight; this.effectiveWeight = effectiveWeight; this.currentWeight = currentWeight; } public String getIp() { return ip; } public void setIp(String ip) { this.ip = ip; } public Integer getWeight() { return weight; } public Integer getEffectiveWeight() { return effectiveWeight; } public void setEffectiveWeight(Integer effectiveWeight) { this.effectiveWeight = effectiveWeight; } public Integer getCurrentWeight() { return currentWeight; } public void setCurrentWeight(Integer currentWeight) { this.currentWeight = currentWeight; } @Override public int compareTo(Node node) { return currentWeight > node.currentWeight ? 1 : (currentWeight.equals(node.currentWeight) ? 0 : -1); } @Override public String toString() { return "{ip='" + ip + "', weight=" + weight + ", effectiveWeight=" + effectiveWeight + ", currentWeight=" + currentWeight + "}"; } }
加權(quán)輪詢算法
/** * 加權(quán)輪詢算法 */public class WeightedRoundRobin { private static List<Node> nodes = new ArrayList<>(); // 權(quán)重之和 private static Integer totalWeight = 0; // 準(zhǔn)備模擬數(shù)據(jù) static { nodes.add(new Node("192.168.1.101",1)); nodes.add(new Node("192.168.1.102",3)); nodes.add(new Node("192.168.1.103",2)); nodes.forEach(node -> totalWeight += node.getEffectiveWeight()); } /** * 按照當(dāng)前權(quán)重(currentWeight)最大值獲取IP * @return Node */ public Node selectNode(){ if (nodes ==null || nodes.size()<=0) return null; if (nodes.size() == 1) return nodes.get(0); Node nodeOfMaxWeight = null; // 保存輪詢選中的節(jié)點(diǎn)信息 synchronized (nodes){ // 打印信息對象:避免并發(fā)時(shí)打印出來的信息太亂,不利于觀看結(jié)果 StringBuffer sb = new StringBuffer(); sb.append(Thread.currentThread().getName()+"==加權(quán)輪詢--[當(dāng)前權(quán)重]值的變化:"+printCurrentWeight(nodes)); // 選出當(dāng)前權(quán)重最大的節(jié)點(diǎn) Node tempNodeOfMaxWeight = null; for (Node node : nodes) { if (tempNodeOfMaxWeight == null) tempNodeOfMaxWeight = node; else tempNodeOfMaxWeight = tempNodeOfMaxWeight.compareTo(node) > 0 ? tempNodeOfMaxWeight : node; } // 必須new個(gè)新的節(jié)點(diǎn)實(shí)例來保存信息,否則引用指向同一個(gè)堆實(shí)例,后面的set操作將會修改節(jié)點(diǎn)信息 nodeOfMaxWeight = new Node(tempNodeOfMaxWeight.getIp(),tempNodeOfMaxWeight.getWeight(),tempNodeOfMaxWeight.getEffectiveWeight(),tempNodeOfMaxWeight.getCurrentWeight()); // 調(diào)整當(dāng)前權(quán)重比:按權(quán)重(effectiveWeight)的比例進(jìn)行調(diào)整,確保請求分發(fā)合理。 tempNodeOfMaxWeight.setCurrentWeight(tempNodeOfMaxWeight.getCurrentWeight() - totalWeight); sb.append(" -> "+printCurrentWeight(nodes)); nodes.forEach(node -> node.setCurrentWeight(node.getCurrentWeight()+node.getEffectiveWeight())); sb.append(" -> "+printCurrentWeight(nodes)); System.out.println(sb); //打印權(quán)重變化過程 } return nodeOfMaxWeight; } // 格式化打印信息 private String printCurrentWeight(List<Node> nodes){ StringBuffer stringBuffer = new StringBuffer("["); nodes.forEach(node -> stringBuffer.append(node.getCurrentWeight()+",") ); return stringBuffer.substring(0, stringBuffer.length() - 1) + "]"; } // 并發(fā)測試:兩個(gè)線程循環(huán)獲取節(jié)點(diǎn) public static void main(String[] args){ Thread thread = new Thread(() -> { WeightedRoundRobin weightedRoundRobin1 = new WeightedRoundRobin(); for(int i=1;i<=totalWeight;i++){ Node node = weightedRoundRobin1.selectNode(); System.out.println(Thread.currentThread().getName()+"==第"+i+"次輪詢選中[當(dāng)前權(quán)重最大]的節(jié)點(diǎn):" + node + "\n"); } }); thread.start(); // WeightedRoundRobin weightedRoundRobin2 = new WeightedRoundRobin(); for(int i=1;i<=totalWeight;i++){ Node node = weightedRoundRobin2.selectNode(); System.out.println(Thread.currentThread().getName()+"==第"+i+"次輪詢選中[當(dāng)前權(quán)重最大]的節(jié)點(diǎn):" + node + "\n"); } } }
執(zhí)行結(jié)果:
main==加權(quán)輪詢--[當(dāng)前權(quán)重]值的變化:[1,3,2] -> [1,-3,2] -> [2,0,4] main==第1次輪詢選中[當(dāng)前權(quán)重最大]的節(jié)點(diǎn):{ip='192.168.1.102', weight=3, effectiveWeight=3, currentWeight=3}
Thread-0==加權(quán)輪詢--[當(dāng)前權(quán)重]值的變化:[2,0,4] -> [2,0,-2] -> [3,3,0] Thread-0==第1次輪詢選中[當(dāng)前權(quán)重最大]的節(jié)點(diǎn):{ip='192.168.1.103', weight=2, effectiveWeight=2, currentWeight=4}
main==加權(quán)輪詢--[當(dāng)前權(quán)重]值的變化:[3,3,0] -> [3,-3,0] -> [4,0,2] main==第2次輪詢選中[當(dāng)前權(quán)重最大]的節(jié)點(diǎn):{ip='192.168.1.102', weight=3, effectiveWeight=3, currentWeight=3}
main==加權(quán)輪詢--[當(dāng)前權(quán)重]值的變化:[4,0,2] -> [-2,0,2] -> [-1,3,4] main==第3次輪詢選中[當(dāng)前權(quán)重最大]的節(jié)點(diǎn):{ip='192.168.1.101', weight=1, effectiveWeight=1, currentWeight=4}
Thread-0==加權(quán)輪詢--[當(dāng)前權(quán)重]值的變化:[-1,3,4] -> [-1,3,-2] -> [0,6,0] Thread-0==第2次輪詢選中[當(dāng)前權(quán)重最大]的節(jié)點(diǎn):{ip='192.168.1.103', weight=2, effectiveWeight=2, currentWeight=4}
main==加權(quán)輪詢--[當(dāng)前權(quán)重]值的變化:[0,6,0] -> [0,0,0] -> [1,3,2] main==第4次輪詢選中[當(dāng)前權(quán)重最大]的節(jié)點(diǎn):{ip='192.168.1.102', weight=3, effectiveWeight=3, currentWeight=6}
Thread-0==加權(quán)輪詢--[當(dāng)前權(quán)重]值的變化:[1,3,2] -> [1,-3,2] -> [2,0,4] Thread-0==第3次輪詢選中[當(dāng)前權(quán)重最大]的節(jié)點(diǎn):{ip='192.168.1.102', weight=3, effectiveWeight=3, currentWeight=3}
main==加權(quán)輪詢--[當(dāng)前權(quán)重]值的變化:[2,0,4] -> [2,0,-2] -> [3,3,0] main==第5次輪詢選中[當(dāng)前權(quán)重最大]的節(jié)點(diǎn):{ip='192.168.1.103', weight=2, effectiveWeight=2, currentWeight=4}
Thread-0==加權(quán)輪詢--[當(dāng)前權(quán)重]值的變化:[3,3,0] -> [3,-3,0] -> [4,0,2] Thread-0==第4次輪詢選中[當(dāng)前權(quán)重最大]的節(jié)點(diǎn):{ip='192.168.1.102', weight=3, effectiveWeight=3, currentWeight=3}
main==加權(quán)輪詢--[當(dāng)前權(quán)重]值的變化:[4,0,2] -> [-2,0,2] -> [-1,3,4] main==第6次輪詢選中[當(dāng)前權(quán)重最大]的節(jié)點(diǎn):{ip='192.168.1.101', weight=1, effectiveWeight=1, currentWeight=4}
Thread-0==加權(quán)輪詢--[當(dāng)前權(quán)重]值的變化:[-1,3,4] -> [-1,3,-2] -> [0,6,0] Thread-0==第5次輪詢選中[當(dāng)前權(quán)重最大]的節(jié)點(diǎn):{ip='192.168.1.103', weight=2, effectiveWeight=2, currentWeight=4}
Thread-0==加權(quán)輪詢--[當(dāng)前權(quán)重]值的變化:[0,6,0] -> [0,0,0] -> [1,3,2] Thread-0==第6次輪詢選中[當(dāng)前權(quán)重最大]的節(jié)點(diǎn):{ip='192.168.1.102', weight=3, effectiveWeight=3, currentWeight=6}
為了方便分析,簡化兩線程執(zhí)行后的結(jié)果
[當(dāng)前權(quán)重]值的變化:[1,3,2] -> [1,-3,2] -> [2,0,4]
[當(dāng)前權(quán)重]值的變化:[2,0,4] -> [2,0,-2] -> [3,3,0]
[當(dāng)前權(quán)重]值的變化:[3,3,0] -> [3,-3,0] -> [4,0,2]
[當(dāng)前權(quán)重]值的變化:[4,0,2] -> [-2,0,2] -> [-1,3,4]
[當(dāng)前權(quán)重]值的變化:[-1,3,4] -> [-1,3,-2] -> [0,6,0]
[當(dāng)前權(quán)重]值的變化:[0,6,0] -> [0,0,0] -> [1,3,2]
[當(dāng)前權(quán)重]值的變化:[1,3,2] -> [1,-3,2] -> [2,0,4]
[當(dāng)前權(quán)重]值的變化:[2,0,4] -> [2,0,-2] -> [3,3,0]
[當(dāng)前權(quán)重]值的變化:[3,3,0] -> [3,-3,0] -> [4,0,2]
[當(dāng)前權(quán)重]值的變化:[4,0,2] -> [-2,0,2] -> [-1,3,4]
[當(dāng)前權(quán)重]值的變化:[-1,3,4] -> [-1,3,-2] -> [0,6,0]
[當(dāng)前權(quán)重]值的變化:[0,6,0] -> [0,0,0] -> [1,3,2]
因?yàn)檎麄€(gè)過程只有當(dāng)前權(quán)重發(fā)生變化,所以分析清楚它就明白了整個(gè)過程。
結(jié)論:
分配完成后當(dāng)前權(quán)重發(fā)生變化,但權(quán)限之和還是等于最初值;
每6輪(1+3+2權(quán)重)就出現(xiàn)權(quán)重全部為0,所以會出現(xiàn)重新循環(huán),6正好等于權(quán)重之和,權(quán)重比等于1/6 : 3/6 : 2/6;
a=權(quán)重1,b=權(quán)重3,c=權(quán)重2,那么權(quán)重變化的6(a+b+c)次中,分配情況為:b c b a c b,很明顯,每個(gè)節(jié)點(diǎn)均勻按權(quán)重分配,節(jié)點(diǎn)分配不再是連續(xù)的。這也是最重要的結(jié)論,正是實(shí)現(xiàn)方式二在文初提到的要實(shí)現(xiàn)的關(guān)鍵點(diǎn)。
該算法在權(quán)重比相差很大時(shí),比如:A=1,B=5,那這個(gè)算法的結(jié)果就跟方式一沒啥區(qū)別了,分配結(jié)果就變成了:{A,B,B,B,B,B},既然沒區(qū)別,那根據(jù)算法復(fù)雜情況,那肯定方式一更好了,所以方式一和方式二可以互補(bǔ),可以根據(jù)權(quán)重比選擇不同的算法。
留下懸念
第一點(diǎn):節(jié)點(diǎn)出現(xiàn)分配失敗時(shí)降低有效權(quán)重值,成功時(shí)提高有效權(quán)重值(但不能大于weight值)的功能。理解了方式二,后面再加這塊功能進(jìn)去就很好理解了;
第二點(diǎn):該算法實(shí)現(xiàn)的背后數(shù)學(xué)證明,用的是什么數(shù)學(xué)理論?
總結(jié)
到此這篇關(guān)于Java負(fù)載均衡算法實(shí)現(xiàn)之輪詢和加權(quán)輪詢的文章就介紹到這了,更多相關(guān)Java輪詢和加權(quán)輪詢算法內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
- 使用Java實(shí)現(xiàn)6種常見負(fù)載均衡算法
- Java實(shí)現(xiàn)5種負(fù)載均衡算法(小結(jié))
- java開發(fā)Dubbo負(fù)載均衡與集群容錯(cuò)示例詳解
- 使用Java實(shí)現(xiàn)5種負(fù)載均衡算法實(shí)例
- Java Grpc實(shí)例創(chuàng)建負(fù)載均衡詳解
- 詳解Java實(shí)現(xiàn)負(fù)載均衡的幾種算法代碼
- Java?Ribbon與openfeign區(qū)別和用法講解
- Java中的服務(wù)發(fā)現(xiàn)與負(fù)載均衡及Eureka與Ribbon的應(yīng)用小結(jié)
相關(guān)文章
Hibernate 與 Mybatis 的共存問題,打破你的認(rèn)知!(兩個(gè)ORM框架)
這篇文章主要介紹了Hibernate 與 Mybatis 如何共存?本文給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2020-08-08Java ArrayList與Vector和LinkedList的使用及源碼分析
ArrayList、Vector、LinkedList類均在java.util包中,均為可伸縮數(shù)組,即可以動(dòng)態(tài)改變長度的數(shù)組。ArrayList 和 Vector都是基于存儲元素的Object[] array來實(shí)現(xiàn)的,它們會在內(nèi)存中開辟一塊連續(xù)的內(nèi)存來存儲2022-11-11springboot+Vue實(shí)現(xiàn)分頁的示例代碼
本文主要介紹了springboot+Vue實(shí)現(xiàn)分頁的示例代碼,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2021-06-06Spring+Mybatis動(dòng)態(tài)切換數(shù)據(jù)源的方法
這篇文章主要為大家詳細(xì)介紹了Spring+Mybatis動(dòng)態(tài)切換數(shù)據(jù)源的方法,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2018-01-01詳解Java編程中包package的內(nèi)容與包對象的規(guī)范
這篇文章主要介紹了Java編程中包package的內(nèi)容與包對象的規(guī)范,是Java入門學(xué)習(xí)中的基礎(chǔ)知識,需要的朋友可以參考下2015-12-12springmvc九大組件之HandlerAdapter詳解
這篇文章主要介紹了springmvc九大組件之HandlerAdapter詳解,RequestMappingHandlerAdapter支持的handler的類型是HandlerMethod,而HandlerMethod是通過解析@RequestMapping注解獲得的,需要的朋友可以參考下2023-11-11spring cloud 使用Hystrix 實(shí)現(xiàn)斷路器進(jìn)行服務(wù)容錯(cuò)保護(hù)的方法
本篇文章主要介紹了spring cloud 使用Hystrix 實(shí)現(xiàn)斷路器進(jìn)行服務(wù)容錯(cuò)保護(hù)的方法,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2018-05-05解決javac不是內(nèi)部或外部命令,也不是可運(yùn)行程序的報(bào)錯(cuò)問題
在學(xué)著使用Java的命令行來編譯java文件的時(shí)候,遇到了這個(gè)問題,本文主要介紹了解決javac不是內(nèi)部或外部命令,也不是可運(yùn)行程序的報(bào)錯(cuò)問題,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2022-04-04