負(fù)載均衡(Load Balance)
模擬IP列表的類
public class IPMap {
// 待路由的IP列表,key代表IP,value代表IP的權(quán)重。
public static HashMap<String, Integer> serverWeightMap = new HashMap<>();
static {
serverWeightMap.put("192.168.1.100", 1);
serverWeightMap.put("192.168.1.101", 4);
serverWeightMap.put("192.168.1.102", 3);
}
}
1. 輪詢法
輪詢調(diào)度算法原理:每一次把來(lái)自用戶的請(qǐng)求輪流分配給內(nèi)部的服務(wù)器。從1開(kāi)始,到N(內(nèi)部服務(wù)器個(gè)數(shù)),然后重新開(kāi)始循環(huán)。
特點(diǎn):簡(jiǎn)潔,無(wú)需記錄當(dāng)前所有連接的狀態(tài),是無(wú)狀態(tài)調(diào)度。
// 輪詢法的參考程序
public class RoundPolling {
private static Integer pos = 0;
public static String getServer() {
// 重建一個(gè)map,避免服務(wù)器的上下線導(dǎo)致的并發(fā)問(wèn)題。
Map<String, Integer> serverMap = new HashMap<>();
// 填充帶權(quán)重的IP列表
serverMap.putAll(IPMap.serverWeightMap);
// 取得IP地址的list
Set<String> keySet = serverMap.keySet();
ArrayList<String> keyList = new ArrayList<>();
keyList.addAll(keySet);
String server = null;
//加鎖(synchronized是重量級(jí)悲觀鎖,該輪詢代碼的并發(fā)吞吐量明顯下降。)
synchronized (pos) {
if (pos > keySet.size()) {
pos = 0;
}
server = keyList.get(pos);
pos++;
}
return server;
}
}
2. 隨機(jī)法
通過(guò)系統(tǒng)的隨機(jī)算法,根據(jù)后端服務(wù)器數(shù)量來(lái)隨機(jī)選擇一臺(tái)服務(wù)器進(jìn)行訪問(wèn)。隨著客戶端調(diào)用服務(wù)器的次數(shù)增多,其實(shí)際效果越來(lái)越接近平均分配,也就是輪詢的結(jié)果。
// 隨機(jī)法的參考程序
public class RandomPolling {
public static String getServer() {
//重建一個(gè)map,避免服務(wù)器的上下線導(dǎo)致的并發(fā)問(wèn)題。
Map<String, Integer> serverMap = new HashMap<>();
serverMap.putAll(IPMap.serverWeightMap);
Set<String> keySet = serverMap.keySet();
ArrayList<String> keyList = new ArrayList<>();
keyList.addAll(keySet);
Random random = new Random();
int rnadomPos = random.nextInt(keyList.size());
return keyList.get(randomPos);
}
}
3. 源地址哈希法
根據(jù)客戶端的IP地址,通過(guò)哈希函數(shù)計(jì)算得到一個(gè)數(shù)值,用該數(shù)值付服務(wù)器列表的大小進(jìn)行取模運(yùn)算,得到的結(jié)果便是客戶端要訪問(wèn)服務(wù)器的序號(hào)。
采用源地址哈希法進(jìn)行負(fù)載均衡,同一IP地址的客戶端,當(dāng)后端服務(wù)器列表不變時(shí),它每次都會(huì)映射到同一臺(tái)后臺(tái)服務(wù)器上。
// 源地址哈希法的參考程序
public class HashPolling {
public static String getServer() {
//重建一個(gè)map,避免服務(wù)器的上下線導(dǎo)致的并發(fā)問(wèn)題。
Map<String, Integer> serverMap = new HashMap<>();
serverMap.putAll(IPMap.serverWeightMap);
Set<String> keySet = serverMap.keySet();
ArrayList<String> keyList = new ArrayList<>();
keyList.addAll(keySet);
// Web中可用HttpServlet.getRemoteIp()方法獲取IP。
String clientIP = "127.0.0.1";
int hashcode = clientIP.hashcode();
int serverListSize = keyList.size();
int serverPos = hashcode % serverListSize;
return keyList.get(serverPos);
}
}
4. 加權(quán)輪詢法
不同的后端服務(wù)器可能配置和系統(tǒng)當(dāng)前的負(fù)載并不一樣,因此服務(wù)器的抗壓能力也不同。
配置高、負(fù)載低的服務(wù)器配置更高的權(quán)重;配置低、負(fù)載低的服務(wù)器配置更低的權(quán)重。將客戶端請(qǐng)求按序且按照權(quán)重分配到后端。
// 加權(quán)輪詢的參考程序
public class RandomPolling {
private static Integer pos;
public static String getServer() {
//重建一個(gè)map,避免服務(wù)器的上下線導(dǎo)致的并發(fā)問(wèn)題。
Map<String, Integer> serverMap = new HashMap<>();
serverMap.putAll(IPMap.serverWeightMap);
Set<String> keySet = serverMap.keySet();
Iterator<String> iterator = keySet.iterator();
List<String> serverList = new ArrayList<>();
while (iterator.hasNext()) {
String server = iterator.next();
int weight = serverMap.get(server);
for (int i = 0; i < weight; i++) { // 根據(jù)權(quán)重大小添加server
serverList.add(server);
}
String server = null;
synchronized (pos) {
if (pos > serverList.size()) {
pos = 0;
}
server = serverList.get(pos);
pos++;
}
return server;
}
}
5. 加權(quán)隨機(jī)法
與加權(quán)輪詢法一樣,加權(quán)隨機(jī)法也根據(jù)后端機(jī)器的配置,系統(tǒng)的負(fù)載分配不同的權(quán)重。不同的是,它是按照權(quán)重隨機(jī)請(qǐng)求后端服務(wù)器,而非順序。
// 加權(quán)輪詢的參考程序
public class RandomPolling {
private static Integer pos;
public static String getServer() {
//重建一個(gè)map,避免服務(wù)器的上下線導(dǎo)致的并發(fā)問(wèn)題。
Map<String, Integer> serverMap = new HashMap<>();
serverMap.putAll(IPMap.serverWeightMap);
Set<String> keySet = serverMap.keySet();
Iterator<String> iterator = keySet.iterator();
List<String> serverList = new ArrayList<>();
while (iterator.hasNext()) {
String server = iterator.next();
int weight = serverMap.get(server);
for (int i = 0; i < weight; i++) { // 根據(jù)權(quán)重大小添加server
serverList.add(server);
}
Random random = new Random();
// 隨機(jī)獲取server序號(hào)
int randomPos = random.nextInt(serverList.size());
return serverList.get(randomPos);
}
}
6. 最小連接數(shù)法
由于后端服務(wù)器的配置不盡相同,對(duì)于請(qǐng)求的處理有快有慢,根據(jù)后端服務(wù)器的連接情況,動(dòng)態(tài)地選擇其中積壓的連接數(shù)最少的服務(wù)器來(lái)處理當(dāng)前的請(qǐng)求。盡可能地提高后端服務(wù)的利用率。從后端服務(wù)器的視角來(lái)觀察系統(tǒng)的負(fù)載。