負載均衡常見算法,你知道幾個?
冷備與熱備
- 冷備份(cool standby),指配備平時不運行的備用設備,當運行設備發生故障時,使用備用設備替換。
- 熱備份(hot standby),指在設備運行的同時運行備用設備,當運行設備發生故障時,能夠自動替換備用設備。
fail-over與fail-back
- fail-over,在空余結構中,停止運行設備,使用備用設備進行工作的過程稱為替換,英文稱為fail-over或者switch-over。
- fail-back,替換后再次恢復到原來的運行設備,也就是從運行狀態的備用設備再切換到原來的運行設備的過程,稱為回退,英文稱為fail-back或switch-back。
冗余類型
1.主備方式(Active-Standby)
準備兩臺路由器,其中一臺作為正常運行業務的活躍設備(active),也可以稱為主設備(master)或者首要設備(primary)。另一臺作為發生故障時替換的備用設備(standby),也可以稱為備機(backup)、從設備(slave)、必要設備(secondary)。活躍設備和備用設備必須共享關于設備的設置信息。
2.雙活方式(Active-Active)
準備兩臺路由器,其中一臺作為首要設備(primary),另一臺作為次要設備(secondary),二者同時運行來組成冗余結構。這種方式可以通過與負載均衡設備并用或者設置DNS、客戶端一側的路由信息來達到負載均衡的目的。
3.集群方式(Cluster)
在主備方式或雙活方式中,使用3臺以上的硬件協同組成冗余結構的方式。
什么是負載均衡
負載均衡,英文名稱為Load Balance,指由多臺服務器以對稱的方式組成一個服務器集合,每臺服務器都具有等價的地位,都可以單獨對外提供服務而無須其他服務器的輔助。通過某種負載分擔技術,將外部發送來的請求均勻分配到對稱結構中的某一臺服務器上,而接收到請求的服務器獨立地回應客戶的請求。負載均衡能夠平均分配客戶請求到服務器陣列,借此提供快速獲取重要數據,解決大量并發訪問服務問題,這種集群技術可以用最少的投資獲得接近于大型主機的性能。
負載均衡算法在很多地方都有使用,無論是在服務治理中或者是在分布式緩存中都大量的使用,本文主要介紹幾種常見的負載均衡的算法.
1.輪詢法
輪詢法,很好理解,將請求按照順序輪流的分配到服務器上,他均衡的對待每一臺后端的服務器,不關心服務器的的連接數和負載情況.以下代碼演示了這種算法.
- public class BalanceServer {
- public static List<String> servers =
- Arrays.asList("192.168.0.1", "192.168.0.2", "192.168.0.3", "192.168.0.4",
- "192.168.0.5");
- public static int pos = 0;
- public static String getServer() {
- String server = null;
- if (pos >= servers.size()) {
- pos = 0;
- }
- server = servers.get(pos);
- pos++;
- return server;
- }
- public static void main(String[] args) {
- for(int i=0;i<10;i++){
- System.out.println(BalanceServer.getServer());
- }
- }
- }
輪詢的策略目的在于請求的絕對均衡,但是在實際的情況下,可能服務器并不是完全一樣,導致有些性能高的服務器不能完全發揮出來.
2.隨機法
通過系統的隨機函數,根據后端服務器列表的大小來隨機獲取其中的一臺來訪問,隨著調用量的增大,實際效果越來越近似于平均分配到沒一臺服務器.和輪詢的效果類似, 代碼如下:
- public class BalanceServer {
- public static List<String> servers = Arrays.asList("192.168.0.1", "192.168.0.2", "192.168.0.3", "192.168.0.4",
- "192.168.0.5");
- public static int pos = 0;
- public static String getServer() {
- String server = null;
- Random random = new Random();
- int randomPos = random.nextInt(servers.size());
- server = servers.get(randomPos);
- return server;
- }
- }
和輪詢算法比較,在并發的場景下,輪詢需要加鎖,隨機法想比而言性能好點.
3.源地址hash法
源地址hash法的思想是獲取客戶端訪問的ip地址,通過hash函數計算出一個hash值,用該hash值對服務器列表的大小進行取模運算,得到的值就是要訪問的服務器的序號,代碼如下:
- public class BalanceServer {
- public static List<String> servers = Arrays.asList("192.168.0.1", "192.168.0.2", "192.168.0.3", "192.168.0.4",
- "192.168.0.5");
- public static int pos = 0;
- public static String getServer(String ip) {
- String server = null;
- int hashCode = ip.hashCode();
- pos = hashCode % servers.size();
- server = servers.get(pos);
- return server;
- }
- }
hash法的好處是,在服務器列表不變的情況下,每次客戶端訪問的服務器都是同一個服務器.利用這個特性可以有狀態的session會話.無需額外的操作就可以實現粘性會話.
4.加權輪詢法
剛剛有說道過,不同的服務器性能不同,所以不能一概而論,需要給性能低的服務器給比較低的權重,性能高的給跟高的權重,代碼如下:
- public class BalanceServer {
- public static Map<String, Integer> serverMap = new HashMap<String, Integer>();
- public static int pos = 0;
- static {
- serverMap.put("192.168.0.1", 1);
- serverMap.put("192.168.0.2", 1);
- serverMap.put("192.168.0.3", 4);
- serverMap.put("192.168.0.4", 3);
- serverMap.put("192.168.0.5", 3);
- serverMap.put("192.168.0.6", 2);
- }
- public static String getServer() {
- Set<String> keySet = serverMap.keySet();
- Iterator<String> it = keySet.iterator();
- List<String> servers = new ArrayList<String>();
- while (it.hasNext()) {
- String server = it.next();
- Integer weight = serverMap.get(server);
- for (int i = 0; i < weight; i++) {
- servers.add(server);
- }
- }
- String server = null;
- if (pos >= servers.size()) {
- pos = 0;
- }
- server = servers.get(pos);
- pos++;
- return server;
- }
- public static void main(String[] args) {
- for(int i=0;i<14;i++){
- System.out.println(BalanceServer.getServer());
- }
- 35 }
- 36}
5.加權隨機法
與加權輪詢法類似,加權隨機法也是根據后端服務器不同的配置和負載情況來配置不同的權重。不同的是,它是按照權重來隨機選擇服務器的,而不是順序。加權隨機法的代碼實現如下:
- public class WeightRandom
- {
- public static String getServer()
- {
- // 重建一個Map,避免服務器的上下線導致的并發問題
- Map<String, Integer> serverMap =
- new HashMap<String, Integer>();
- serverMap.putAll(IpMap.serverWeightMap);
- // 取得Ip地址List
- Set<String> keySet = serverMap.keySet();
- Iterator<String> iterator = keySet.iterator();
- List<String> serverList = new ArrayList<String>();
- while (iterator.hasNext())
- {
- String server = iterator.next();
- int weight = serverMap.get(server);
- for (int i = 0; i < weight; i++)
- serverList.add(server);
- }
- java.util.Random random = new java.util.Random();
- int randomPos = random.nextInt(serverList.size());
- return serverList.get(randomPos);
- }
- }