您好,登錄后才能下訂單哦!
這篇文章主要介紹“服務器的負載均衡算法怎么實現”,在日常操作中,相信很多人在服務器的負載均衡算法怎么實現問題上存在疑惑,小編查閱了各式資料,整理出簡單好用的操作方法,希望對大家解答”服務器的負載均衡算法怎么實現”的疑惑有所幫助!接下來,請跟著小編一起來學習吧!
負載平衡(Load balancing)是一種在多個計算機(網絡、CPU、磁盤)之間均勻分配資源,以提高資源利用的技術。使用負載均衡可以最大化服務吞吐量,可能最小化響應時間,同時由于使用負載均衡時,會使用多個服務器節點代單點服務,也提高了服務的可用性。
負載均衡的實現可以軟件可以硬件,硬件如大名鼎鼎的 F5 負載均衡設備,軟件如 NGINX 中的負載均衡實現,又如 Springcloud Ribbon 組件中的負載均衡實現。
如果看到這里你還不知道負載均衡是干嘛的,那么只能放一張圖了,畢竟沒圖說個啥。
負載均衡要做到在多次請求下,每臺服務器被請求的次數大致相同。但是實際生產中,可能每臺機器的性能不同,我們會希望性能好的機器承擔的請求更多一些,這也是正常需求。
如果這樣說下來你看不懂,那我就再舉個例子好了,一排可愛的小熊(服務器)站好。
這時有人(用戶)要過來打臉(請求訪問)。
那么怎么樣我們才能讓這每一個可愛的小熊被打的次數大致相同呢?
又或者熊 4 比較胖,抗擊打能力是別人的兩倍,我們怎么提高熊 4 被打的次數也是別人的兩倍呢?
又或者每次出手的力度不同,有重有輕,恰巧熊 4 總是承受這種大力度啪啪打臉,熊 4 即將不省熊事,還要繼續打它嗎?
這些都是值的思考的問題。
說了那么多,口干舌燥,我雙手已經饑渴難耐了,迫不及待的想要擼起代碼了。
1. 隨機訪問
上面說了,為了負載均衡,我們必須保證多次出手后,熊 1 到熊 4 被打次數均衡。比如使用隨機訪問法,根據數學上的概率論,隨機出手次數越多,每只熊被打的次數就會越相近。代碼實現也比較簡單,使用一個隨機數,隨機訪問一個就可以了。
/** 服務器列表 */
private static List<String> serverList =
new ArrayList<>();
static {
serverList.add("192.168.1.2");
serverList.add("192.168.1.3");
serverList.add("192.168.1.4");
serverList.add("192.168.1.5");
}
/**
* 隨機路由算法
*/
public static String
random(
) {
// 復制遍歷用的集合,防止操作中集合有變更
List<String> tempList = new ArrayList<>(serverList.size());
tempList.addAll(serverList);
// 隨機數隨機訪問
int randomInt = new Random().nextInt(tempList.size());
return tempList.get(randomInt);
}
因為使用了非線程安全的集合,所以在訪問操作時操作的是集合的拷貝,下面幾種輪訓方式中也是這種思想。
寫一個模擬請求方法,請求10w次,記錄請求結果。
public static void main(String[] args) {
HashMap<String, Integer> serverMap =
new HashMap<>();
for (int i =
0; i <
20000; i++) {
String server = random();
Integer count = serverMap.get(server);
if (count ==
null) {
count =
1;
}
else {
count++;
}
// 記錄
serverMap.put(server, count);
}
// 路由總體結果
for (Map.Entry<String, Integer> entry : serverMap.entrySet()) {
System.out.println("IP:" + entry.getKey() + ",次數:" + entry.getValue());
}
}
運行得到請求結果。
IP:192.168.1.3,次數:24979
IP:192.168.1.2,次數:24896
IP:192.168.1.5,次數:25043
IP:192.168.1.4,次數:25082
每臺服務器被訪問的次數都趨近于 2.5w,有點負載均衡的意思。但是隨機畢竟是隨機,是不能保證訪問次數絕對均勻的。
2. 輪訓訪問
輪訓訪問就簡單多了,拿上面的熊1到熊4來說,我們一個接一個的啪啪 - 打臉,熊1打完打熊2,熊2打完打熊3,熊4打完打熊1,最終也是實現了被打均衡。但是保證均勻總是要付出代價的,隨機訪問中需要隨機,輪訓訪問中需要什么來保證輪訓呢?
/** 服務器列表 */
private static List<String> serverList =
new ArrayList<>();
static {
serverList.add("192.168.1.2");
serverList.add("192.168.1.3");
serverList.add("192.168.1.4");
serverList.add("192.168.1.5");
}
private static Integer index =
0;
/**
* 隨機路由算法
*/
public static String
randomOneByOne(
) {
// 復制遍歷用的集合,防止操作中集合有變更
List<String> tempList = new ArrayList<>(serverList.size());
tempList.addAll(serverList);
String server = "";
synchronized (index) {
index++;
if (index == tempList.size()) {
index = 0;
}
server = tempList.get(index);;
}
return server;
}
由代碼里可以看出來,為了保證輪訓,必須記錄上次訪問的位置,為了讓在并發情況下不出現問題,還必須在使用位置記錄時進行加鎖,很明顯這種互斥鎖增加了性能開銷。
依舊使用上面的測試代碼測試10w次請求負載情況。
IP:192.168.1.3,次數:25000
IP:192.168.1.2,次數:25000
IP:192.168.1.5,次數:25000
IP:192.168.1.4,次數:25000
3. 輪訓加權
上面演示了輪訓方式,還記的一開始提出的熊4比較胖抗擊打能力強,可以承受別人2倍的挨打次數嘛?上面兩種方式都沒有體現出來熊 4 的這個特點,熊 4 竊喜,不痛不癢。但是熊 1 到 熊 3 已經在崩潰的邊緣,不行,我們必須要讓胖著多打,能者多勞,提高整體性能。
/** 服務器列表 */
private static HashMap<String, Integer> serverMap =
new HashMap<>();
static {
serverMap.put("192.168.1.2",
2);
serverMap.put("192.168.1.3",
2);
serverMap.put("192.168.1.4",
2);
serverMap.put("192.168.1.5",
4);
}
private static Integer index =
0;
/**
* 加權路由算法
*/
public static String
oneByOneWithWeight(
) {
List<String> tempList =
new ArrayList();
HashMap<String, Integer> tempMap =
new HashMap<>();
tempMap.putAll(serverMap);
for (String key : serverMap.keySet()) {
for (int i =
0; i < serverMap.get(key); i++) {
tempList.add(key);
}
}
synchronized (index) {
index++;
if (index == tempList.size()) {
index =
0;
}
return tempList.get(index);
}
}
這次記錄下了每臺服務器的整體性能,給出一個數值,數值越大,性能越好。可以承受的請求也就越多,可以看到服務器 192.168.1.5 的性能為 4,是其他服務器的兩倍,依舊 10 w 請求測試。
IP:192.168.1.3,次數:20000
IP:192.168.1.2,次數:20000
IP:192.168.1.5,次數:40000
IP:192.168.1.4,次數:20000
192.168.1.5 承擔了 2 倍的請求。
4. 隨機加權
隨機加權的方式和輪訓加權的方式大致相同,只是把使用互斥鎖輪訓的方式換成了隨機訪問,按照概率論來說,訪問量增多時,服務訪問也會達到負載均衡。
/** 服務器列表 */
private static HashMap<String, Integer> serverMap =
new HashMap<>();
static {
serverMap.put("192.168.1.2",
2);
serverMap.put("192.168.1.3",
2);
serverMap.put("192.168.1.4",
2);
serverMap.put("192.168.1.5",
4);
}
/**
* 加權路由算法
*/
public static String
randomWithWeight(
) {
List<String> tempList =
new ArrayList();
HashMap<String, Integer> tempMap =
new HashMap<>();
tempMap.putAll(serverMap);
for (String key : serverMap.keySet()) {
for (int i =
0; i < serverMap.get(key); i++) {
tempList.add(key);
}
}
int randomInt =
new Random().nextInt(tempList.size());
return tempList.get(randomInt);
}
依舊 10 w 請求測試,192.168.1.5 的權重是其他服務器的近似兩倍,
IP:192.168.1.3,次數:19934
IP:192.168.1.2,次數:20033
IP:192.168.1.5,次數:39900
IP:192.168.1.4,次數:20133
5. IP-Hash
上面的幾種方式要么使用隨機數,要么使用輪訓,最終都達到了請求的負載均衡。但是也有一個很明顯的缺點,就是同一個用戶的多次請求很有可能不是同一個服務進行處理的,這時問題來了,如果你的服務依賴于 session ,那么因為服務不同, session 也會丟失,不是我們想要的,所以出現了一種根據請求端的 ip 進行哈希計算來決定請求到哪一臺服務器的方式。這種方式可以保證同一個用戶的請求落在同一個服務上。
private static List<String> serverList =
new ArrayList<>();
static {
serverList.add("192.168.1.2");
serverList.add("192.168.1.3");
serverList.add("192.168.1.4");
serverList.add("192.168.1.5");
}
/**
* ip hash 路由算法
*/
public static String
ipHash(String ip) {
// 復制遍歷用的集合,防止操作中集合有變更
List<String> tempList = new ArrayList<>(serverList.size());
tempList.addAll(serverList);
// 哈希計算請求的服務器
int index = ip.hashCode() % serverList.size();
return tempList.get(Math.abs(index));
}
到此,關于“服務器的負載均衡算法怎么實現”的學習就結束了,希望能夠解決大家的疑惑。理論與實踐的搭配能更好的幫助大家學習,快去試試吧!若想繼續學習更多相關知識,請繼續關注億速云網站,小編會繼續努力為大家帶來更多實用的文章!
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。