什么是負載均衡
由于微服務架構的廣泛應用,對于負載較高的服務來說,往往對應著多臺服務器組成的集群。請求來臨時,為了將請求均衡的分配到后端服務器,負載均衡程序將從注冊中心查詢可用的服務節點,通過負載均衡算法和規則,選取一臺服務器進行訪問,這個過程稱為服務的負載均衡。
為什么要使用負載均衡
讓所有節點以最小的代價、最好的狀態對外提供服務,快速獲取重要數據,最大化降低了單個節點過載、甚至crash的概率,解決大量并發訪問服務問題;
負載均衡算法
輪詢法
將請求順序輪流分配到節點上;
隨機法
根據節點列表的大小,隨機選擇一臺進行訪問,當請求無限大的時候,效果等同于平均分配;
源地址哈希法
根據客戶端的IP生成一個hash值,與節點列表大小進行取模,當節點列表不發生改變的時候,請求恒打在固定服務器上;
加權輪詢法
不同服務器的配置不同,為其設置權重,使得能者多勞;
加權隨機法
同加權輪詢法
最小連接法
由于后端服務器的配置不同,對于請求響應速度也不同,每次選擇請求積壓條數最少的服務器被選中;
自適應最優選擇方法
這種算法的主要思路是在客戶端本地維護一份同每一個服務節點的性能統計快照,并且每隔一段時間去更新這個快照。在發起請求時,根據“二八原則”,把服務節點分成兩部分,找出 20% 的那部分響應最慢的節點,然后降低權重。這樣的話,客戶端就能夠實時的根據自身訪問每個節點性能的快慢,動態調整訪問最慢的那些節點的權重,來減少訪問量,從而可以優化長尾請求。由此可見,自適應最優選擇算法是對加權輪詢算法的改良,可以看作是一種動態加權輪詢算法。它的實現關鍵之處就在于兩點:第一點是每隔一段時間獲取客戶端同每個服務節點之間調用的平均性能統計;第二點是按照這個性能統計對服務節點進行排序,對排在性能倒數 20% 的那部分節點賦予一個較低的權重,其余的節點賦予正常的權重。
//負載均衡算法
public class LoadBalancingTest {
public static final Map<String,Integer> serverWeightMap = new HashMap<>();
static {
serverWeightMap.put("127.0.0.1",1);
serverWeightMap.put("127.0.0.2",1);
serverWeightMap.put("127.0.0.3",5);
serverWeightMap.put("127.0.0.4",1);
serverWeightMap.put("127.0.0.5",1);
serverWeightMap.put("127.0.0.6",1);
}
public static Integer pos = 0;
public static void main(String[] args) {
String server = "";
//輪詢法
server = polling();
server = random();
server = ipHash("127.0.0.1");
server = weightedPolling();
server = weightedRandom();
server = leastConnection();
System.out.println(server);
}
//輪詢法
private static String polling(){
//重新創建一個map,避免出現由于服務器上線與下線造成的并發問題
Map<String,Integer> serverMap = new HashMap<>();
serverMap.putAll(serverWeightMap);
//獲取IP地址list
Set<String> keySet = serverMap.keySet();
ArrayList<String> keyList = new ArrayList<>();
keyList.addAll(keySet);
String server = null;
synchronized (pos){
if (pos >= keySet.size()){
pos = 0;
}
server = keyList.get(pos);
pos++;
}
return server;
}
//隨機法
private static String random(){
//重新創建一個map,避免出現由于服務器上線與下線造成的并發問題
Map<String,Integer> serverMap = new HashMap<>();
serverMap.putAll(serverWeightMap);
//獲取IP地址list
Set<String> keySet = serverMap.keySet();
ArrayList<String> keyList = new ArrayList<>();
keyList.addAll(keySet);
Random random = new Random();
int randomPos = random.nextInt(keySet.size());
String server = keyList.get(randomPos);
return server;
}
//源地址哈希法
private static String ipHash(String ip){
//重新創建一個map,避免出現由于服務器上線與下線造成的并發問題
Map<String,Integer> serverMap = new HashMap<>();
serverMap.putAll(serverWeightMap);
//獲取IP地址list
Set<String> keySet = serverMap.keySet();
ArrayList<String> keyList = new ArrayList<>();
keyList.addAll(keySet);
int hashCode = ip.hashCode();
int serverPos = hashCode%keySet.size();//keyset 不為null
String server = keyList.get(serverPos);
return server;
}
//加權輪詢法
private static String weightedPolling(){
//重新創建一個map,避免出現由于服務器上線與下線造成的并發問題
Map<String,Integer> serverMap = new HashMap<>();
serverMap.putAll(serverWeightMap);
//獲取IP地址list
Set<String> keySet = serverMap.keySet();
Iterator<String> it = keySet.iterator();
ArrayList<String> keyList = new ArrayList<>();
String server = null;
while (it.hasNext()){
server = it.next();
Integer weight = serverMap.get(server);
for (int i = 0 ; i < weight ; i++){
keyList.add(server);
}
}
server = null;
synchronized (pos){
if (pos >= keySet.size()){
pos = 0;
}
server = keyList.get(pos);
pos++;
}
return server;
}
//加權隨機法
private static String weightedRandom(){
//重新創建一個map,避免出現由于服務器上線與下線造成的并發問題
Map<String,Integer> serverMap = new HashMap<>();
serverMap.putAll(serverWeightMap);
//獲取IP地址list
Set<String> keySet = serverMap.keySet();
Iterator<String> it = keySet.iterator();
ArrayList<String> keyList = new ArrayList<>();
String server = null;
while (it.hasNext()){
server = it.next();
Integer weight = serverMap.get(server);
for (int i = 0 ; i < weight ; i++){
keyList.add(server);
}
}
Random random = new Random();
int randomPos = random.nextInt(keyList.size());
server = keyList.get(randomPos);
return server;
}
//最小連接法,將權重作為連接數在該方法中使用
private static String leastConnection(){
//重新創建一個map,避免出現由于服務器上線與下線造成的并發問題
Map<String,Integer> serverMap = new HashMap<>();
serverMap.putAll(serverWeightMap);
String server = null;
int minConnections = -1;
for (String key : serverMap.keySet()){
if (serverMap.get(key)<minConnections){
minConnections = serverMap.get(key);
server = key;
}
}
serverMap.put(server,minConnections);
serverWeightMap.put(server,minConnections);
return server;
}
}