服務提供者對象構建類html
public class Refer { private String serverName; /** * 當前使用該referer的調用數 * * @return */ private int activeRefererCount; /** * 連接是否可用 */ private boolean isAvailable; /** * 類路徑 */ private String serviceKey; /** * 方法名 */ private String method; /** * 提供權重佔比 */ private int weight; public int getActiveRefererCount() { return activeRefererCount; } public void setActiveRefererCount(int activeRefererCount) { this.activeRefererCount = activeRefererCount; } public boolean isAvailable() { return isAvailable; } public void setAvailable(boolean available) { isAvailable = available; } public String getServerName() { return serverName; } public void setServerName(String serverName) { this.serverName = serverName; } public String getServiceKey() { return serviceKey; } public void setServiceKey(String serviceKey) { this.serviceKey = serviceKey; } public String getMethod() { return method; } public void setMethod(String method) { this.method = method; } public int getWeight() { return weight; } public void setWeight(int weight) { this.weight = weight; } @Override public String toString() { return "Refer{" + "serverName=" + serverName + ", activeRefererCount=" + activeRefererCount + ", isAvailable=" + isAvailable + ", serviceKey='" + serviceKey + '\'' + ", method='" + method + '\'' + ", weight=" + weight + '}'; } }
方案一:輪詢權重算法實現類算法
public class RoundRobinWeightLoadBalance { public static List<Refer> refers = new ArrayList<Refer>(); private static final ConcurrentMap<String, AtomicPositiveInteger> sequences = new ConcurrentHashMap<>(); private static int[] weights = new int[] {5, 1, 1}; private static String[] names = new String[] {"A", "B", "C"}; static { for (int i = 1; i < 4; i++) { Refer refer = new Refer(); refer.setServerName(names[i-1]); refer.setActiveRefererCount(ThreadLocalRandom.current().nextInt(11)); refer.setAvailable(ThreadLocalRandom.current().nextInt(2) == 1 ? true : false); refer.setMethod("sayHello"); refer.setServiceKey("com.zzx.DemoService"); refer.setWeight(weights[i-1]); refers.add(refer); } } private static Refer roundRobinWeight() { String key = refers.get(0).getServiceKey() + "." + refers.get(0).getMethod(); int length = refers.size(); //最大權重 int maxWeight = 0; //最小權重 int minWeight = Integer.MAX_VALUE; final LinkedHashMap<Refer, IntegerWrapper> referToWeightMap = new LinkedHashMap<>(); //權重總和 int weightSum = 0; //下面這個循環主要用於查找最大和最小權重,計算權重總和 for (int i = 0; i < length; i++) { int weight = refers.get(i).getWeight(); //獲取權重最大和最小值 maxWeight = Math.max(maxWeight, weight); minWeight = Math.min(minWeight, weight); if(weight > 0) { referToWeightMap.put(refers.get(i), new IntegerWrapper(weight)); //累加權重 weightSum += weight; } } //獲取當前服務對應的調用序列對象 AtomicPositiveInteger,默認爲0 AtomicPositiveInteger sequence = sequences.get(key); if(sequence == null) { sequences.putIfAbsent(key, new AtomicPositiveInteger()); sequence = sequences.get(key); } //獲取當前的調用編號 int currentSequence = sequence.getAndIncrement(); //若是最小權重小於最大權重,代表服務提供者之間的權重是不相等的 if(maxWeight > 0 && minWeight < maxWeight) { //使用調用編號對權重總和進行取餘操做 int mod = currentSequence % weightSum; //進行maxWeight次遍歷 for(int i = 0; i < maxWeight; i++) { //遍歷 invokerToWeightMap for (Map.Entry<Refer, IntegerWrapper> each : referToWeightMap.entrySet()) { final Refer k = each.getKey(); //獲取權重包裝類數據 final IntegerWrapper v = each.getValue(); //若是 mod = 0, 且權重大於0, 此時返回相應的Invoker if(mod == 0 && v.getValue() > 0) { return k; } //mod != 0,且權重大於0,此時權重和mod分別進行自減操做 if(v.getValue() > 0) { v.decrement(); mod--; } } } } return refers.get(currentSequence%length); } private static final class IntegerWrapper { private int value; public IntegerWrapper(int value) { this.value = value; } public void decrement(){ this.value--; } public int getValue() { return value; } public void setValue(int value) { this.value = value; } } public static void main(String[] args) { for (Refer refer : refers) { System.out.println(refer); } System.out.println("---------------------------------------------"); for (int i = 0; i < 8; i++) { System.out.println("獲取按權重輪詢Refer:" + roundRobinWeight()); } } }
測試結果:apache
總結:該算法權重設置比較大,mod比較大,會致使循環次數比較多,嚴重影響性能,得到Refer對象的性能。服務器
方案二輪詢權重算法以下:併發
public class RoundRobinWeightLoadBalance2 { public static List<Refer> refers = new ArrayList<Refer>(); private static final ConcurrentMap<String, AtomicPositiveInteger> sequences = new ConcurrentHashMap<>(); private static final ConcurrentMap<String, AtomicPositiveInteger> indexSeqs = new ConcurrentHashMap<String, AtomicPositiveInteger>(); private static int[] weights = new int[] {5, 1, 1}; private static String[] names = new String[] {"A", "B", "C"}; static { for (int i = 1; i < 4; i++) { Refer refer = new Refer(); refer.setServerName(names[i-1]); refer.setActiveRefererCount(ThreadLocalRandom.current().nextInt(11)); refer.setAvailable(ThreadLocalRandom.current().nextInt(2) == 1 ? true : false); refer.setMethod("sayHello"); refer.setServiceKey("com.zzx.DemoService"); refer.setWeight(weights[i-1]); refers.add(refer); } } private static Refer roundRobinWeight() { String key = refers.get(0).getServiceKey() + "." + refers.get(0).getMethod(); int length = refers.size(); //最大權重 int maxWeight = 0; //最小權重 int minWeight = Integer.MAX_VALUE; final List<Refer> invokerToWeightList = new ArrayList<>(); //下面這個循環主要用於查找最大和最小權重,計算權重總和 for (int i = 0; i < length; i++) { int weight = refers.get(i).getWeight(); //獲取權重最大和最小值 maxWeight = Math.max(maxWeight, weight); minWeight = Math.min(minWeight, weight); if(weight > 0) { invokerToWeightList.add(refers.get(i)); } } //獲取當前服務對應的調用序列對象 AtomicPositiveInteger,默認爲0 AtomicPositiveInteger sequence = sequences.get(key); if(sequence == null) { sequences.putIfAbsent(key, new AtomicPositiveInteger()); sequence = sequences.get(key); } // 獲取下標序列對象 AtomicPositiveInteger AtomicPositiveInteger indexSeq = indexSeqs.get(key); if (indexSeq == null) { // 建立 AtomicPositiveInteger,默認值爲 -1 indexSeqs.putIfAbsent(key, new AtomicPositiveInteger(-1)); indexSeq = indexSeqs.get(key); } if (maxWeight > 0 && minWeight < maxWeight) { length = invokerToWeightList.size(); while (true) { //經過循環,依次獲取list的下標 int index = indexSeq.incrementAndGet() % length; //得到每個請求,當前權重值 int currentWeight = sequence.get() % maxWeight; // 每循環一輪(index = 0),從新計算 currentWeight if (index == 0) { currentWeight = sequence.incrementAndGet() % maxWeight; } // 檢測 Invoker 的權重是否大於 currentWeight,大於則返回 if (invokerToWeightList.get(index).getWeight() > currentWeight) { return invokerToWeightList.get(index); } } } return refers.get(sequence.incrementAndGet() % length); } private static final class IntegerWrapper { private int value; public IntegerWrapper(int value) { this.value = value; } public void decrement(){ this.value--; } public int getValue() { return value; } public void setValue(int value) { this.value = value; } } public static void main(String[] args) { for (Refer refer : refers) { System.out.println(refer); } System.out.println("---------------------------------------------"); for (int i = 0; i < 8; i++) { System.out.println("獲取按權重輪詢Refer:" + roundRobinWeight()); } } }
測試結果app
總結負載均衡
雖然該方案解決權重大時,產生性能的問題。可是從測試結果發現,會致使ServiceA在某一個時刻大量請求併發,增大服務器A的壓力。dom
針對方案二的問題可參考nignx,輪詢權重算法實現,使用平滑加權實現。ide
Nginx 的平滑加權輪詢負載均衡。每一個服務器對應兩個權重,分別爲 weight 和 currentWeight。其中 weight 是固定的,currentWeight 會動態調整,初始值爲0。當有新的請求進來時,遍歷服務器列表,讓它的 currentWeight 加上自身權重。遍歷完成後,找到最大的 currentWeight,並將其減去權重總和,而後返回相應的服務器便可。性能
上面描述不是很好理解,下面仍是舉例進行說明。這裏仍然使用服務器 [A, B, C] 對應權重 [5, 1, 1] 的例子說明,如今有7個請求依次進入負載均衡邏輯,選擇過程以下:
參考連接:
http://dubbo.apache.org/zh-cn/docs/source_code_guide/loadbalance.html