對於作開發的同窗來講,負載均衡算法已經不陌生了,今天一塊兒來盤點一下分佈式系統中都是有哪些負載均衡算法以及它的優缺點;算法
思想: 將請求按順序輪流地分配到後端服務器上,它均衡地對待後端每一臺服務器,而不關心服務實際的鏈接數和當前系統的負載;
代碼實現:後端
private List<String> list = new CopyOnWriteArrayList(); private volatile Integer pos = 0; { list.add("127.0.0.1"); list.add("127.0.0.2"); list.add("127.0.0.3"); } public String getServer() { if (null == list || list.size() <= 0) { return null; } String server = null; synchronized (pos) { if (pos >= list.size()) { pos = 0; } server = list.get(pos++); } return server; }
總結:服務器
這種算法簡單,依次轉發,每一個服務器的請求數量平均;
缺點:當集羣中的服務器的性能有差異時,沒法區別對待的狀況下會形成資源浪費;負載均衡
思想: 經過系統隨機函數,根據後端服務器列表大小值來隨機選取其中一臺進行訪問;隨着調用量的增大,效果接近輪詢算法;
代碼實現:dom
private List<String> list = new CopyOnWriteArrayList(); { list.add("127.0.0.1"); list.add("127.0.0.2"); list.add("127.0.0.3"); list.add("127.0.0.4"); } public String getServerRandom() { if (null == list || list.size() <= 0) { return null; } Random random = new Random(); String server = list.get(random.nextInt(list.size())); return server; }
總結:分佈式
算法雖然簡單,可是在大請求量的狀況下才能保證均衡ide
思想: 源地址哈希的思想是獲取客戶端訪問的ip地址,經過hash函數計算獲得一個值,用該值從服務器列表中進行取模運算;當服務器列表不變時,同一ip老是請求到同一臺服務器中;
代碼實現:函數
private List<String> list = new CopyOnWriteArrayList(); { list.add("127.0.0.1"); list.add("127.0.0.2"); list.add("127.0.0.3"); list.add("127.0.0.4"); } public String getServerHash(String hostIp) { if (null == list || list.size() <= 0 || null == hostIp) { return null; } int code = hostIp.hashCode(); int serverPos = list.size() % code; return list.get(serverPos); }
思想: 與輪詢算法相比,它加了權重,權重超高的服務,接收到有請求越多;
代碼實現:性能
private ConcurrentMap<String, Integer> hosts = new ConcurrentHashMap<>(); private volatile Integer pos = 0; { hosts.put("127.0.0.1", 1); hosts.put("127.0.0.2", 2); hosts.put("127.0.0.3", 2); hosts.put("127.0.0.4", 1); } public String getServerRoundRobin() { List<String> list = new ArrayList<>(); for (Map.Entry<String, Integer> entry : hosts.entrySet()) { Integer value = entry.getValue(); for (int i = 0; i < value; i++) { list.add(entry.getKey()); } } String server = null; synchronized (pos) { if (pos >= list.size()) { pos = 0; } server = list.get(pos++); } return server; }
思想: 是在隨機的基礎上,加上權值;
代碼實現:3d
private ConcurrentMap<String, Integer> hosts = new ConcurrentHashMap<>(); private volatile Integer pos = 0; { hosts.put("127.0.0.1", 1); hosts.put("127.0.0.2", 2); hosts.put("127.0.0.3", 2); hosts.put("127.0.0.4", 1); } public String getServerRandomWeight() { List<String> list = new ArrayList<>(); for (Map.Entry<String, Integer> entry : hosts.entrySet()) { Integer value = entry.getValue(); for (int i = 0; i < value; i++) { list.add(entry.getKey()); } } Random random = new Random(); String server = list.get(random.nextInt(list.size())); return server; }
上面兩種實現加權的方式都是權重爲幾,就往list裏面add幾回,若是服務器數量之龐大,會致使list列表過大;有另一種實現加權的方式,把每臺服務和權重劃分爲一段,權重越大,佔的段長越長:
代碼實現:
private Map<String, Integer> hosts = new ConcurrentHashMap<>(); private volatile Integer pos = 0; { hosts.put("127.0.0.1", 1); hosts.put("127.0.0.2", 2); hosts.put("127.0.0.3", 2); hosts.put("127.0.0.4", 1); } public String getServerRandomWeight2() { // 累加全部權重 int sum = 0; for (Map.Entry<String, Integer> entry : hosts.entrySet()) { sum += entry.getValue(); } Random random = new Random(); int index = random.nextInt(sum); for (Map.Entry<String, Integer> entry : hosts.entrySet()) { Integer value = entry.getValue(); if(value >= index){ return entry.getKey(); } index -= entry.getValue(); } return null; }
思想: 最小鏈接數法是根據服務器當前的鏈接狀況進行負載均衡的,它會選擇一臺鏈接數最少的機器來提供服務;
代碼實現:
// key:機器ip value:當前訪問量 private Map<String, Integer> hosts = new ConcurrentHashMap<>(); private volatile Integer pos = 0; { hosts.put("127.0.0.1", 6); hosts.put("127.0.0.2", 2); hosts.put("127.0.0.3", 3); hosts.put("127.0.0.4", 8); } public String getServerLeastConnection() { // 尋找最小鏈接 int min = 0; String key = null; for (Map.Entry<String, Integer> entry : hosts.entrySet()) { if (entry.getValue() < min) { min = entry.getValue(); key = entry.getKey(); } } hosts.put(key, min + 1); return key; }
注:文中代碼實現不適合真實場景,只是爲了簡單易懂理解算法思想;