1. 程式人生 > >java分散式之負載均衡

java分散式之負載均衡

在傳統的系統中,應用程式、資料伺服器、檔案伺服器等都在同一臺機器上部署。隨著網際網路的發展,系統的訪問量的增加,一臺機器根本滿足不了多使用者的高併發訪問,

計算機領域,當單機效能達到瓶頸時,有兩種方式可以解決效能問題,一是堆硬體,進一步提升配置,二是分散式,水平擴充套件。所以,假如一臺機器不能

滿足高併發的請求訪問,那麼就兩臺,兩臺還滿足不了就增加到三臺,這種方式我們稱之為水平擴充套件,如何實現請求的平均分配便是負載均衡

負載均衡,英文名稱(Load Balance),簡稱 LB。指由多臺伺服器以對稱的方式組成一個伺服器集合,每臺伺服器都具有等價的地位,都可以單獨對外提供服務而無須其他服

務器的輔助。通過某種負載分擔技術,將外部發送來的請求均勻分配到對稱結構中的某一臺伺服器上,而接收到請求的伺服器獨立地迴應客戶的請求。負載均衡能夠

平均分配客戶請求到服 務器陣列,藉此提供快速獲取重要資料,解決大量併發訪問服務問題,這種叢集技術可以用最少的投資獲得接近於大型主機的效能。

那麼如何來實現把請求均勻地分配到不同的分散式伺服器上呢。這裡介紹幾種演算法:

首先我們先定義一個map的IP地址,指每臺伺服器

 public class IpMap
 2 {
 3     // 待路由的Ip列表,Key代表Ip,Value代表該Ip的權重
 4     public static HashMap<String, Integer> serverWeightMap = 
 5             new HashMap<String, Integer>();
 6     
 7     static
 8     {
 9         serverWeightMap.put("192.168.1.100", 1);
10         serverWeightMap.put("192.168.1.101", 1);
11         // 權重為4
12         serverWeightMap.put("192.168.1.102", 4);
13         serverWeightMap.put("192.168.1.103", 1);
14         serverWeightMap.put("192.168.1.104", 1);
15         // 權重為3
16         serverWeightMap.put("192.168.1.105", 3);
17         serverWeightMap.put("192.168.1.106", 1);
18         // 權重為2
19         serverWeightMap.put("192.168.1.107", 2);
20         serverWeightMap.put("192.168.1.108", 1);
21         serverWeightMap.put("192.168.1.109", 1);
22         serverWeightMap.put("192.168.1.110", 1);
23     }
24 }

1、輪詢演算法

public class RoundRobin
 2 {
 3     private static Integer pos = 0;
 4     
 5     public static String getServer()
 6     {
 7         // 重建一個Map,避免伺服器的上下線導致的併發問題
 8         Map<String, Integer> serverMap = 
 9                 new HashMap<String, Integer>();
10         serverMap.putAll(IpMap.serverWeightMap);
11         
12         // 取得Ip地址List
13         Set<String> keySet = serverMap.keySet();
14         ArrayList<String> keyList = new ArrayList<String>();
15         keyList.addAll(keySet);
16         
17         String server = null;
18         synchronized (pos)
19         {
20             if (pos > keySet.size())
21                 pos = 0;
22             server = keyList.get(pos);
23             pos ++;
24         }
25         
26         return server;
27     }
28 }

由於serverWeightMap中的伺服器可能會出現新增、下線或者宕機,為了避免可能出現的併發問題(?),方法內部要新建區域性變數serverMap,現將

serverMap中的內容複製到執行緒本地,以避免被多 個執行緒修改。這樣可能會引入新的問題,複製以後serverWeightMap的修改無法反映給serverMap,也就是

說這一輪選擇伺服器的過程中, 新增伺服器或者下線伺服器,負載均衡演算法將無法獲知。新增無所謂,如果有伺服器下線或者宕機,那麼可能會訪問到不存在的

地址。因此,服務呼叫端需要有相應的容錯處理,比如重新發起一次server選擇並呼叫對於當前輪詢的位置變數pos,為了保證伺服器選擇的順序性,需要在操

作時對其加鎖,使得同一時刻只能有一個執行緒可以修改pos的值。

2、隨機法

 public class Random
 2 {
 3     public static String getServer()
 4     {
 5         // 重建一個Map,避免伺服器的上下線導致的併發問題
 6         Map<String, Integer> serverMap = 
 7                 new HashMap<String, Integer>();
 8         serverMap.putAll(IpMap.serverWeightMap);
 9         
10         // 取得Ip地址List
11         Set<String> keySet = serverMap.keySet();
12         ArrayList<String> keyList = new ArrayList<String>();
13         keyList.addAll(keySet);
14         
15         java.util.Random random = new java.util.Random();
16         int randomPos = random.nextInt(keyList.size());
17         
18         return keyList.get(randomPos);
19     }
20 }

在選取server的時候,通過Random的nextInt方法取0~keyList.size()區間的一個隨機值,然後獲得伺服器並返回。

3、雜湊地址法

public class Hash
 2 {
 3     public static String getServer()
 4     {
 5         // 重建一個Map,避免伺服器的上下線導致的併發問題
 6         Map<String, Integer> serverMap = 
 7                 new HashMap<String, Integer>();
 8         serverMap.putAll(IpMap.serverWeightMap);
 9         
10         // 取得Ip地址List
11         Set<String> keySet = serverMap.keySet();
12         ArrayList<String> keyList = new ArrayList<String>();
13         keyList.addAll(keySet);
14         
15         // 在Web應用中可通過HttpServlet的getRemoteIp方法獲取
16         String remoteIp = "127.0.0.1";
17         int hashCode = remoteIp.hashCode();
18         int serverListSize = keyList.size();
19         int serverPos = hashCode % serverListSize;
20         
21         return keyList.get(serverPos);
22     }
23 }
雜湊演算法就是通過客戶端的IP地址,取得它的hash值,然後對伺服器列表個數取模,來取得相應的伺服器。

總結:

輪詢演算法:優點是基本能夠實現請求的均衡分配;缺點是為了使得服務分配的順序性,必須得引入悲觀鎖(synchronized),影響併發訪問的效率。

隨機演算法:基於概率統計原理,吞吐量越大,隨機發越能接近輪詢法的平均效果。

hash演算法:保證了相同客戶端IP地址將會被雜湊到同一臺後端伺服器,直到後端伺服器列表變更。根據此特性可以在服務消費者與服務提供者之間建立有狀態的

session會話。缺點是如果伺服器發生上下線或者宕機的情況,會造成不能正確路由到以前所路由到的伺服器上,如果是session則取不到session,如果是快取則

可能引發"雪崩"

4、加權輪詢法或加權隨機法

另外:由於不同伺服器的配置不同,抗壓能力也不盡相同,那麼為了給那些配置好的多些負載,配置低的伺服器少些負載,就引入了權重的概念。

public class WeightRoundRobin
 2 {
 3     private static Integer pos;
 4     
 5     public static String getServer()
 6     {
 7         // 重建一個Map,避免伺服器的上下線導致的併發問題
 8         Map<String, Integer> serverMap = 
 9                 new HashMap<String, Integer>();
10         serverMap.putAll(IpMap.serverWeightMap);
11         
12         // 取得Ip地址List
13         Set<String> keySet = serverMap.keySet();
14         Iterator<String> iterator = keySet.iterator();
15         
16         List<String> serverList = new ArrayList<String>();
17         while (iterator.hasNext())
18         {
19             String server = iterator.next();
20             int weight = serverMap.get(server);
21             for (int i = 0; i < weight; i++)
22                 serverList.add(server);
23         }
24         
25         String server = null;
26         synchronized (pos)
27         {
28             if (pos > keySet.size())
29                 pos = 0;
30             server = serverList.get(pos);
31             pos ++;
32         }
33         
34         return server;
35     }
36 }

關鍵是在獲得伺服器之前加入了下面的權重程式碼,如果權重大,在serverList裡面的該服務就多,就能多分得幾次請求。

while (iterator.hasNext())
18         {
19             String server = iterator.next();
20             int weight = serverMap.get(server);
21             for (int i = 0; i < weight; i++)
22                 serverList.add(server);
23         }