關(guān)于Java中常見(jiàn)的負(fù)載均衡算法
負(fù)載均衡
負(fù)載平衡(Load balancing)是一種電子計(jì)算機(jī)技術(shù),用來(lái)在多個(gè)計(jì)算機(jī)(計(jì)算機(jī)集群)、網(wǎng)絡(luò)連接、CPU、磁盤(pán)驅(qū)動(dòng)器或其他資源中分配負(fù)載,以達(dá)到優(yōu)化資源使用、最大化吞吐率、最小化響應(yīng)時(shí)間、同時(shí)避免過(guò)載的目的。
使用帶有負(fù)載平衡的多個(gè)服務(wù)器組件,取代單一的組件,可以通過(guò)冗余提高可靠性。
負(fù)載平衡服務(wù)通常是由專(zhuān)用軟件和硬件來(lái)完成。
主要作用是將大量作業(yè)合理地分?jǐn)偟蕉鄠€(gè)操作單元上進(jìn)行執(zhí)行,用于解決互聯(lián)網(wǎng)架構(gòu)中的高并發(fā)和高可用的問(wèn)題。
常見(jiàn)的負(fù)載均衡算法
1.輪詢(Round Robin)
輪詢算法按照順序?qū)⑿碌恼?qǐng)求分配給下一個(gè)服務(wù)器,最終實(shí)現(xiàn)平分請(qǐng)求。
實(shí)例:已知服務(wù)器: s1 ,s2, s3
請(qǐng)求1 -> s1
請(qǐng)求2-> s2
請(qǐng)求3 -> s3
請(qǐng)求4 -> s1
請(qǐng)求5 -> s2
請(qǐng)求6 -> s3
…
優(yōu)點(diǎn):
- ? 實(shí)現(xiàn)簡(jiǎn)單,無(wú)需記錄各種服務(wù)的狀態(tài),是一種無(wú)狀態(tài)的負(fù)載均衡策略。
- ? 實(shí)現(xiàn)絕對(duì)公平
缺點(diǎn):
- 當(dāng)各個(gè)服務(wù)器性能不一致的情況,無(wú)法根據(jù)服務(wù)器性能去分配,無(wú)法合理利用服務(wù)器資源。
java實(shí)現(xiàn)輪詢算法:
思路:根據(jù)上面的介紹,依次的選擇下一個(gè)服務(wù)器,輪詢算法具有周期性的特性,這就是典型的周期性概念,我們第一想法應(yīng)該就是取余了。
這里推薦大家《程序員的數(shù)學(xué)1》里面介紹了一些數(shù)學(xué)和編程思維的一些案例,其中就有介紹周期和分組的思想,個(gè)人感覺(jué)這本書(shū)還是不錯(cuò)的,推薦給大家。
public class RoundRobin { @Data public static class Server { private int serverId; private String name; private int weight; public Server(int serverId, String name) { this.serverId = serverId; this.name = name; } public Server(int serverId, String name, int weight) { this.serverId = serverId; this.name = name; this.weight = weight; } } private static AtomicInteger NEXT_SERVER_COUNTER = new AtomicInteger(0); private static int select(int modulo) { for (; ; ) { int current = NEXT_SERVER_COUNTER.get(); int next = (current + 1) % modulo; boolean compareAndSet = NEXT_SERVER_COUNTER.compareAndSet(current, next); if (compareAndSet) { return next; } } } public static Server selectServer(List<Server> serverList) { return serverList.get(select(serverList.size())); } public static void main(String[] args) { List<Server> serverList = new ArrayList<>(); serverList.add(new Server(1, "服務(wù)器1")); serverList.add(new Server(2, "服務(wù)器2")); serverList.add(new Server(3, "服務(wù)器3")); for (int i = 0; i < 10; i++) { Server selectedServer = selectServer(serverList); System.out.format("第%d次請(qǐng)求,選擇服務(wù)器%s\n", i + 1, selectedServer.toString()); } } }
2.加權(quán)輪詢(WeightedRound-Robin)
由于不同的服務(wù)器配置不同,因此它們處理請(qǐng)求的能力也不同,給配置高的機(jī)器配置相對(duì)較高的權(quán)重,讓其處理更多的請(qǐng)求,給配置較低的機(jī)器配置較低的權(quán)重減輕期負(fù)載壓力。
加權(quán)輪詢可以較好的解決這個(gè)問(wèn)題。
思路:
根據(jù)權(quán)重的大小讓其獲得相應(yīng)被輪詢到的機(jī)會(huì)。
已知:
服務(wù)器 | 權(quán)重 |
s1 | 1 |
s2 | 2 |
s3 | 3 |
可以根據(jù)權(quán)重我們?cè)趦?nèi)存中創(chuàng)建一個(gè)這樣的數(shù)組{s1,s2,s2,s3,s3,s3},然后再按照輪詢的方式選擇相應(yīng)的服務(wù)器。
缺點(diǎn):
- 請(qǐng)求被分配到三臺(tái)服務(wù)器上機(jī)會(huì)不夠平滑。
- 前3次請(qǐng)求都不會(huì)落在server3上。
Nginx實(shí)現(xiàn)了一種平滑的加權(quán)輪詢算法,可以將請(qǐng)求平滑(均勻)的分配到各個(gè)節(jié)點(diǎn)上。
下面我們用Java實(shí)現(xiàn)一下這個(gè)算法。
實(shí)現(xiàn)思路
我們以當(dāng)前節(jié)點(diǎn)權(quán)重作為被選中的概率
public void incrCurrentWeight() { this.currentWeight += weight; }
為了避免權(quán)重大的被連續(xù)選中,所以再被選中的時(shí)候我們應(yīng)該讓其的當(dāng)前權(quán)重變小。我們可以采用
//當(dāng)前權(quán)重 = 當(dāng)前權(quán)重 - 總權(quán)重
1-6 =-5
3-6 =-3
可得權(quán)重越大下次當(dāng)前權(quán)重變成最大的可能性也越大
public void selected(int total) { this.currentWeight -= total; }
我們選取當(dāng)前當(dāng)前權(quán)重最大的一個(gè)服務(wù)器
public class WeightRoundRobin { @Data public static class Server { private int serverId; private String name; private int weight; private int currentWeight; public Server(int serverId, String name) { this.serverId = serverId; this.name = name; } public Server(int serverId, String name, int weight) { this.serverId = serverId; this.name = name; this.weight = weight; } public void selected(int total) { this.currentWeight -= total; } public void incrCurrentWeight() { this.currentWeight += weight; } } public static Server selectServer(List<Server> serverList) { int total = 0; Server selectedServer = null; int maxWeight = 0; for (Server server : serverList) { total += server.getWeight(); server.incrCurrentWeight(); //選取當(dāng)前權(quán)重最大的一個(gè)服務(wù)器 if (selectedServer == null || maxWeight < server.getCurrentWeight()) { selectedServer = server; maxWeight = server.getCurrentWeight(); } } if (selectedServer == null){ Random random = new Random(); int next = random.nextInt(serverList.size()); return serverList.get(next); } selectedServer.selected(total); return selectedServer; } public static void main(String[] args) { List<Server> serverList = new ArrayList<>(); serverList.add(new Server(1, "服務(wù)器1", 1)); serverList.add(new Server(2, "服務(wù)器2", 3)); serverList.add(new Server(3, "服務(wù)器3", 10)); for (int i = 0; i < 10; i++) { Server server = selectServer(serverList); System.out.format("第%d次請(qǐng)求,選擇服務(wù)器%s\n", i + 1, server.toString()); } }
3.隨機(jī)(Random)
思路:利用隨機(jī)數(shù)從所有服務(wù)器中隨機(jī)選取一臺(tái),可以用服務(wù)器數(shù)組下標(biāo)獲取。
public class RandomLoadBalance { @Data public static class Server { private int serverId; private String name; private int weight; public Server(int serverId, String name) { this.serverId = serverId; this.name = name; } } public static Server selectServer(List<Server> serverList) { Random selector = new Random(); int next = selector.nextInt(serverList.size()); return serverList.get(next); } public static void main(String[] args) { List<Server> serverList = new ArrayList<>(); serverList.add(new Server(1, "服務(wù)器1")); serverList.add(new Server(2, "服務(wù)器2")); serverList.add(new Server(3, "服務(wù)器3")); for (int i = 0; i < 10; i++) { Server selectedServer = selectServer(serverList); System.out.format("第%d次請(qǐng)求,選擇服務(wù)器%s\n", i + 1, selectedServer.toString()); } } }
4.加權(quán)隨機(jī)(Weight Random)
思路:
這里我們是利用區(qū)間的思想,通過(guò)一個(gè)小于在此區(qū)間范圍內(nèi)的一個(gè)隨機(jī)數(shù),選中對(duì)應(yīng)的區(qū)間(服務(wù)器),區(qū)間越大被選中的概率就越大。
已知:
服務(wù)器 | 權(quán)重 |
s1 | 1 |
s2 | 2 |
s3 | 3 |
那么:
s1:[0,1] s2:(1,3] s3 (3,6]
public class WeightRandom { @Data public static class Server { private int serverId; private String name; private int weight; public Server(int serverId, String name) { this.serverId = serverId; this.name = name; } public Server(int serverId, String name, int weight) { this.serverId = serverId; this.name = name; this.weight = weight; } } private static Server selectServer(List<Server> serverList) { int sumWeight = 0; for (Server server : serverList) { sumWeight += server.getWeight(); } Random serverSelector = new Random(); int nextServerRange = serverSelector.nextInt(sumWeight); int sum = 0; Server selectedServer = null; for (Server server : serverList) { if (nextServerRange >= sum && nextServerRange < server.getWeight() + sum) { selectedServer = server; } sum += server.getWeight(); } return selectedServer; } public static void main(String[] args) { List<Server> serverList = new ArrayList<>(); serverList.add(new Server(1, "服務(wù)器1", 1)); serverList.add(new Server(2, "服務(wù)器2", 5)); serverList.add(new Server(3, "服務(wù)器3", 10)); for (int i = 0; i < 10; i++) { Server selectedServer = selectServer(serverList); System.out.format("第%d次請(qǐng)求,選擇服務(wù)器%s\n", i + 1, selectedServer.toString()); } } }
5.IPHash
思路:根據(jù)每個(gè)每個(gè)請(qǐng)求ip(也可以是某個(gè)標(biāo)識(shí))ip.hash() % server.size()
public class IpHash { @Data public static class Server { private int serverId; private String name; public Server(int serverId, String name) { this.serverId = serverId; this.name = name; } } public static Server selectServer(List<Server> serverList, String ip) { int ipHash = ip.hashCode(); return serverList.get(ipHash % serverList.size()); } public static void main(String[] args) { List<Server> serverList = new ArrayList<>(); serverList.add(new Server(1, "服務(wù)器1")); serverList.add(new Server(2, "服務(wù)器2")); serverList.add(new Server(3, "服務(wù)器3")); List<String> ips = Arrays.asList("192.168.9.5", "192.168.9.2", "192.168.9.3"); for (int i = 0; i < 10; i++) { for (String ip : ips) { Server selectedServer = selectServer(serverList, ip); System.out.format("請(qǐng)求ip:%s,選擇服務(wù)器%s\n", ip, selectedServer.toString()); } } } }
可以看到結(jié)果:同一ip肯定會(huì)命中同一臺(tái)機(jī)器。
到此這篇關(guān)于關(guān)于Java中常見(jiàn)的負(fù)載均衡算法的文章就介紹到這了,更多相關(guān)Java負(fù)載均衡算法內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
使用java實(shí)現(xiàn)云端資源共享小程序的代碼
這篇文章主要介紹了用java寫(xiě)一個(gè)云端資源共享小程序,本文通過(guò)實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2020-07-07Java內(nèi)部類(lèi)的全限定名規(guī)律代碼示例
這篇文章主要介紹了Java內(nèi)部類(lèi)的全限定名規(guī)律代碼示例,具有一定借鑒價(jià)值,需要的朋友可以參考下。2017-12-12IDEA如何使用spring-Initializr快速搭建SpringBoot
這篇文章主要介紹了IDEA如何使用spring-Initializr快速搭建SpringBoot問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2024-05-05SpringBoot實(shí)現(xiàn)定時(shí)任務(wù)和異步調(diào)用
這篇文章主要為大家詳細(xì)介紹了SpringBoot實(shí)現(xiàn)定時(shí)任務(wù)和異步調(diào)用,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2019-04-04java實(shí)現(xiàn)文件和base64相互轉(zhuǎn)換
這篇文章主要為大家詳細(xì)介紹了java如何實(shí)現(xiàn)文件和base64相互轉(zhuǎn)換,文中的示例代碼講解詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴可以跟隨小編一起學(xué)習(xí)一下2023-11-11springboot通用分支處理超級(jí)管理員權(quán)限邏輯
這篇文章主要為大家介紹了springboot通用分支處理超級(jí)管理員的權(quán)限邏輯,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-07-07SpringMVC實(shí)現(xiàn)注解式權(quán)限驗(yàn)證的實(shí)例
本篇文章主要介紹了SpringMVC實(shí)現(xiàn)注解式權(quán)限驗(yàn)證的實(shí)例,可以使用Spring MVC中的action攔截器來(lái)實(shí)現(xiàn),具有一定的參考價(jià)值,有興趣的可以了解下。2017-02-02