負載均衡算法在很多地方都有使用,無論是在服務治理中或者是在分布式緩存中都大量的使用,本文主要介紹幾種常見的負載均衡的算法.
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會話.無需額外的操作就可以實現粘性會話.
- 加權輪詢法.
剛剛有說道過,不同的服務器性能不同,所以不能一概而論,需要給性能低的服務器給比較低的
權重,性能高的給跟高的權重.
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());
}
}
}
5.加權隨機法,
加權隨機法算法和加權輪詢法類似.就不多說了.
- 有關hash算法的一些補充說明
在上面的hash算法中,存在以下的幾個問題
1.當一臺服務器宕機了或者新添加一臺機器之后,這個時候hashCode % servers.size()
需要重新計算hash值, 如果在緩存的環境中,所有的請求都會涌向數據庫服務
器,給數據庫服務器帶來巨大的壓力,可能導致整個系統不可用,形成雪崩效應.
2 .當新增了一臺性能強的機器后,利用上述的hash算法無法讓,新增的性能強的服務器多承擔壓力.
基于上面的幾個問題,提出了hash算法的改進,consistent hash算法,consistent hashing
也是一種 hash 算法,簡單的說,在移除 / 添加操作,它能夠盡可能小的改變已存在 key 映射關系.
consistent hash算法的原理是它將hash函數的值域組織成一個
環形,整個空間按照順時針的方式進行組織,將對應的服務器節點進行hash,將他們映射到
hash環上,假設有四臺機器node1-4,hash之后如圖所示:
consistent hashing.png
接下來使用相同的hash函數,計算出對應的key值和hash值,按照順時針的方式,分布在node1和node2
的key,訪問時被定位在node2,分布在node2和node4的key被定位在node4上,以此類推.假設現
在新增一個node5,假設hash之后在node2和node4之間,如圖所示:
新增node.png
那么受影響的節點只有node2和node5,他們將會從新hash,而其他的key的映射將不會變化.
當然,上面描繪了一種很理想的情況,各個節點在環上分布的十分均勻.正常情況下,當節點數量少的
時候,節點分布并不均勻,這時需要引入虛擬節點機制.
總結:本篇只是概述了幾種常見的均衡負載算法,有關 consistent hash算法,可以參考http://blog.csdn.net/sparkliang/article/details/5279393