1. 程式人生 > >負載均衡總結

負載均衡總結

最近在學習負載均衡原理,看了大量的文章和相關知識,有很大的收穫,做一個簡要總結,一來回顧自己所學的知識,二來對所看所學知識整理,供後來者參考借鑑,如有錯誤的地方,還請指出,學生階段,考慮的內容尚有欠缺之處。

1.什麼是負載均衡

負載均衡,英文名稱為Load Balance,指由多臺伺服器以對稱的方式組成一個伺服器集合,每臺伺服器都具有等價的地位,都可以單獨對外提供服務而無須其他伺服器的輔助。通過某種負載分擔技術,將外部發送來的請求均勻分配到對稱結構中的某一臺伺服器上,而接收到請求的伺服器獨立地迴應客戶的請求。負載均衡能夠平均分配客戶請求到伺服器陣列,藉此提供快速獲取重要資料,解決大量併發訪問服務問題,這種叢集技術可以用最少的投資獲得接近於大型主機的效能。
這裡寫圖片描述

負載均衡系統架構如上圖所示,其實際是代理模式的實現。來自客戶端的請求經由負載均衡器,按照某種均衡演算法分配到後端伺服器進行響應。後端伺服器處理後,將響應報文返回負載均衡伺服器,負載均衡伺服器再返回客戶端。

負載均衡在服務端開發中算是一個比較重要的特性。負載均衡分為:

  • 硬體負載均衡

    專用的軟體和硬體相結合的裝置,裝置商會提供完整成熟的解決方案,通常也會更加昂貴,例如:思傑。

  • 軟體負載均衡

    • 四層負載均衡(基於IP地址轉發)
      四層負載均衡工作在OSI模型的傳輸層,主要工作是轉發,它在接收到客戶端的流量以後通過修改資料包的地址資訊將流量轉發到應用伺服器。
    • 七層負載均衡(基於應用層協議轉發)
      七層負載均衡工作在OSI模型的應用層,因為它需要解析應用層流量,所以七層負載均衡在接到客戶端的流量以後,還需要一個完整的TCP/IP協議棧。七層負載均衡會與客戶端建立一條完整的連線並將應用層的請求流量解析出來,再按照排程演算法選擇一個應用伺服器,並與應用伺服器建立另外一條連線將請求傳送過去,因此七層負載均衡的主要工作就是代理。

既然四層負載均衡做的主要工作是轉發,那就存在一個轉發模式的問題,目前主要有四層轉發模式:DR模式、NAT模式、TUNNEL模式。三種模式將在下文詳細介紹。

2.IPVS實現機制

1) VS/NAT:即(VirtualServer via Network Address Translation).

即網路地址翻譯技術實現虛擬伺服器,當用戶請求到達排程器時,排程器將請求報文的目標地址(即虛擬IP地址)改寫成選定的RealServer地址,同時報文的目標埠也改成選定的RealServer的相應埠,最後將報文請求傳送到選定的RealServer。在伺服器端得到資料後,RealServer返回資料給使用者時,需要再次經過負載排程器將報文的源地址和源埠改成虛擬IP地址和相應埠,然後把資料傳送給使用者,完成整個負載排程過程。可以看出,在NAT方式下,使用者請求和響應報文都必須經過DirectorServer地址重寫,當用戶請求越來越多時,排程器的處理能力將稱為瓶頸。

優點:叢集中的物理伺服器可以使用任何支援TCP/IP作業系統,物理伺服器可以分配Internet的保留私有地址,只有負載均衡器需要一個合法的IP地址。

缺點:擴充套件性有限。當伺服器節點(普通PC伺服器)資料增長到20
個或更多時,負載均衡器將成為整個系統的瓶頸,因為所有的請求包和應答包都需要經過負載均衡器再生。假使TCP包的平均長度是536位元組的話,平均包再生延遲時間大約為60us(在Pentium處理器上計算的,採用更快的處理器將使得這個延遲時間變短),負載均衡器的最大容許能力為93M/s,假定每臺物理伺服器的平臺容許能力為400K/s來計算,負責均衡器能為22臺物理伺服器計算。

2)VS/TUN :即(Virtual Server via IP Tunneling).

IP隧道技術實現虛擬伺服器。它的連線排程和管理與VS/NAT方式一樣,只是它的報文轉發方法不同,VS/TUN方式中,排程器採用IP隧道技術將使用者請求轉發到某個RealServer,而這個RealServer將直接響應使用者的請求,不再經過前端排程器,此外,對RealServer的地域位置沒有要求,可以和DirectorServer位於同一個網段,也可以是獨立的一個網路。因此,在TUN方式中,排程器將只處理使用者的報文請求,集群系統的吞吐量大大提高。

優點:負載均衡器只負責將請求包分發給物理伺服器,而物理伺服器將應答包直接發給使用者。所以,負載均衡器能處理很巨大的請求量,這種方式,一臺負載均衡能為超過100臺的物理伺服器服務,負載均衡器不再是系統的瓶頸。使用VS-TUN方式,如果你的負載均衡器擁有100M的全雙工網絡卡的話,就能使得整個VirtualServer能達到1G的吞吐量。

缺點:這種方式需要所有的伺服器支援”IP Tunneling”(IP Encapsulation)協議,僅在Linux系統上實現了。

3) VS/DR:即(VirtualServer via Direct Routing).

即用直接路由技術實現虛擬伺服器。它的連線排程和管理與VS/NAT和VS/TUN中的一樣,但它的報文轉發方法又有不同,VS/DR通過改寫請求報文的MAC地址,將請求傳送到RealServer,而RealServer將響應直接返回給客戶,免去了VS/TUN中的IP隧道開銷。這種方式是三種負載排程機制中效能最高最好的,但是必須要求DirectorServer與RealServer都有一塊網絡卡連在同一物理網段上。

優點:和VS-TUN一樣,負載均衡器也只是分發請求,應答包通過單獨的路由方法返回給客戶端。與VS-TUN相比,VS-DR這種實現方式不需要隧道結構,因此可以使用大多數作業系統做為物理伺服器,

缺點:要求負載均衡器的網絡卡必須與物理網絡卡在一個物理段上

3.負載均衡策略

(1)HTTP重定向策略

當用戶發來請求的時候,Web伺服器通過修改HTTP響應頭中的Location標記來返回一個新url,然後瀏覽器再繼續請求這個新url,實際上就是頁面重定向。通過重定向,來達到“負載均衡”的目標這個方式非常容易實現,並且可以自定義各種策略,但是,它在大規模訪問量下,效能不佳,而且,給使用者的體驗也不好,實際請求發生重定向,增加了網路延時所以此方式瞭解即可,實際應用較少

(2) 反向代理策略.

反向代理服務的核心工作主要是轉發HTTP請求,扮演了瀏覽器端和後臺Web伺服器中轉的角色。因為它工作在HTTP層(應用層),也就是網路七層結構中的第七層,因此也被稱為“七層負載均衡”。可以做反向代理的軟體很多,比較常見的一種是Nginx,Nginx是一種非常靈活的反向代理軟體,可以自由定製化轉發策略,分配伺服器流量的權重等

優點:實現和部署非常簡單,效能也很好,可以方便的自定義轉發規則

缺點:有“單點故障”的問題,如果掛了,會帶來很多的麻煩,而且,隨著Web伺服器繼續增加,它本身可能成為系統的瓶頸

(3)IP負載均衡策略.

原理:它是對IP層的資料包的IP地址和埠資訊進行修改,達到負載均衡的目的在負載均衡伺服器收到客戶端的IP包的時候,會修改IP包的目標IP地址或埠,然後原封不動地投遞到內部網路中,資料包會流入到實際Web伺服器。實際伺服器處理完成後,又會將資料包投遞迴給負載均衡伺服器,它再修改目標IP地址為使用者IP地址,最終回到客戶端。因為它工作在網路層,也就是網路七層結構中的第4層,因此也被稱為“四層負載均衡”。常見的負載均衡方式,是LVS(LinuxVirtual Server,Linux虛擬服務),通過IPVS(IP Virtual Server,IP虛擬服務)來實現。

優點:效能比反向代理負載均衡高很多,非常穩定缺點:功能單一,配置複雜

(4)DNS負載均衡策略.

DNS(Domain Name System)負責域名解析的服務,域名url實際上是伺服器的別名,實際對映是一個IP地址,解析過程,就是DNS完成域名到IP的對映。而一個域名是可以配置成對應多個IP的。因此,DNS也就可以作為負載均衡服務優點:配置簡單,效能極佳缺點:不能自由定義規則,而且,變更被對映的IP或者機器故障時很麻煩,還存在DNS生效延遲的問題

4.負載均衡演算法

(1)隨機法

通過系統隨機函式,根據後端伺服器列表的大小值來隨機選擇其中一臺進行訪問。由概率統計理論可以得知,隨著呼叫量的增大,其實際效果越來越接近於平均分配流量到每一臺後端伺服器,也就是輪詢的效果。

隨機法的程式碼實現大致如下:

public class Random
{
   public static String getServer()
   {
       // 重建一個Map,避免伺服器的上下線導致的併發問題
       Map<String, Integer> serverMap = 
               new HashMap<String,Integer>();
       serverMap.putAll(IpMap.serverWeightMap);
        // 取得Ip地址List
       Set<String> keySet = serverMap.keySet();
       ArrayList<String> keyList = newArrayList<String>();
       keyList.addAll(keySet);
        java.util.Randomrandom = new java.util.Random();
       int randomPos = random.nextInt(keyList.size());
        returnkeyList.get(randomPos);
   }
}

整體程式碼思路和輪詢法一致,先重建serverMap,再獲取到server列表。在選取server的時候,通過Random的nextInt方法取0~keyList.size()區間的一個隨機值,從而從伺服器列表中隨機獲取到一臺伺服器地址進行返回。基於概率統計的理論,吞吐量越大,隨機演算法的效果越接近於輪詢演算法的效果。

(2)輪詢(RoundRobin)法

原理:負載均衡伺服器建立一張伺服器map,客戶端請求到來,按照順序依次分配給後端伺服器處理。

public class RoundRobin
{
   private static Integer pos = 0;
    public static String getServer()
   {
       // 重建一個Map,避免伺服器的上下線導致的併發問題
       Map<String, Integer> serverMap = 
               new HashMap<String,Integer>();
       serverMap.putAll(IpMap.serverWeightMap);

       // 取得Ip地址List
       Set<String> keySet = serverMap.keySet();
       ArrayList<String> keyList = newArrayList<String>();
       keyList.addAll(keySet);

       String server = null;
       synchronized (pos)
       {
           if (pos > keySet.size())
               pos = 0;
           server = keyList.get(pos);
           pos ++;
       }

       return server;
   }
}

由於serverWeightMap中的地址列表是動態的,隨時可能有機器上線、下線或者宕機,因此為了避免可能出現的併發問題,方法內部要新建區域性變數serverMap,現將serverMap中的內容複製到執行緒本地,以避免被多個執行緒修改。這樣可能會引入新的問題,複製以後serverWeightMap的修改無法反映給serverMap,也就是說這一輪選擇伺服器的過程中,新增伺服器或者下線伺服器,負載均衡演算法將無法獲知。新增無所謂,如果有伺服器下線或者宕機,那麼可能會訪問到不存在的地址。因此,服務呼叫端需要有相應的容錯處理,比如重新發起一次server選擇並呼叫。

對於當前輪詢的位置變數pos,為了保證伺服器選擇的順序性,需要在操作時對其加鎖,使得同一時刻只能有一個執行緒可以修改pos的值,否則當pos變數被併發修改,則無法保證伺服器選擇的順序性,甚至有可能導致keyList陣列越界。

優點:試圖做到請求轉移的絕對均衡。

缺點:為了做到請求轉移的絕對均衡,必須付出相當大的代價,因為為了保證pos變數修改的互斥性,需要引入重量級的悲觀鎖synchronized,這將會導致該段輪詢程式碼的併發吞吐量發生明顯的下降。

(3)加權輪詢法

輪詢法負載均衡基於請求數的合理平衡分配,實現負載均衡。但這存在一種問題:對於後端伺服器A、B,A的配置較B伺服器較高,高併發到來n個請求,按照輪詢法原理,A、B伺服器各分配n/2各請求,但由於A伺服器配置較高,很快處理完請求,而B伺服器並沒有處理完所有請求,當再次到來m個請求時,再次分包分配m/2個請求,長此下去,B伺服器積聚較多的請求。這其實並沒有達到均衡負載的目的,系統處於“偽均衡”狀態。

不同的伺服器可能機器配置和當前系統的負載並不相同,因此它們的抗壓能力也不盡相同,給配置高、負載低的機器配置更高的權重,讓其處理更多的請求,而低配置、高負載的機器,則給其分配較低的權重,降低其系統負載。加權輪詢法可以很好地處理這一問題,並將請求順序按照權重分配到後端。

public class WeightRoundRobin
{
   private static Integer pos;

   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 = newArrayList<String>();
       while (iterator.hasNext())
       {
           String server = iterator.next();
           int weight = serverMap.get(server);
           for (int i = 0; i < weight; i++)
               serverList.add(server);
       }

       String server = null;
       synchronized (pos)
       {
           if (pos > keySet.size())
               pos = 0;
           server = serverList.get(pos);
           pos ++;
       }
        return server;
   }
}  

與輪詢法類似,只是在獲取伺服器地址之前增加了一段權重計算的程式碼,根據權重的大小,將地址重複地增加到伺服器地址列表中,權重越大,該伺服器每輪所獲得的請求數量越多。

(4)最小連線數

最小連線數演算法基於後端伺服器狀態(未處理請求佇列大小)進行合理分配方式,每次客戶端請求到來,負載均衡伺服器從伺服器map中,選取連線數最小的後端伺服器進行分配請求。

優點:解決輪詢演算法偽均衡狀態。
缺點:同一客戶端,由於當時伺服器的狀態不同,每次請求到來可能會分配到不同的後端伺服器,這就對會話一致性帶來困難。

(5)IP雜湊法
源地址雜湊的思想是獲取客戶端訪問的IP地址值,通過雜湊函式計算得到一個數值,用該數值對伺服器列表的大小進行取模運算,得到的結果便是要訪問的伺服器的序號。源地址雜湊演算法的程式碼實現大致如下:

public class Hash
{
   public static String getServer()
   {
       // 重建一個Map,避免伺服器的上下線導致的併發問題
       Map<String, Integer> serverMap = 
               new HashMap<String,Integer>();
       serverMap.putAll(IpMap.serverWeightMap);

       // 取得Ip地址List
       Set<String> keySet = serverMap.keySet();
       ArrayList<String> keyList = newArrayList<String>();
       keyList.addAll(keySet);

       // 在Web應用中可通過HttpServlet的getRemoteIp方法獲取
       String remoteIp = "127.0.0.1";
       int hashCode = remoteIp.hashCode();
       int serverListSize = keyList.size();
       int serverPos = hashCode % serverListSize;
        returnkeyList.get(serverPos);
   }
}  

前兩部分和輪詢法、隨機法一樣就不說了,差別在於路由選擇部分。通過客戶端的ip也就是remoteIp,取得它的Hash值,對伺服器列表的大小取模,結果便是選用的伺服器在伺服器列表中的索引值。

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

缺點:除非叢集中伺服器的非常穩定,基本不會上下線,否則一旦有伺服器上線、下線,那麼通過源地址雜湊演算法路由到的伺服器是伺服器上線、下線前路由到的伺服器的概率非常低,如果是session則取不到session,如果是快取則可能引發”雪崩”。如果這麼解釋不適合明白,可以看我之前的一篇文章MemCache超詳細解讀,一致性Hash演算法部分。

5.會話一致性

使用者(瀏覽器)在和服務端互動的時候,通常會在本地儲存一些資訊,而整個過程叫做一個會話(Session)並用唯一的Session ID進行標識。會話的概念不僅用於購物車這種常見情況,因為HTTP協議是無狀態的,所以任何需要邏輯上下文的情形都必須使用會話機制,此外HTTP客戶端也會額外快取一些資料在本地,這樣就可以減少請求提高效能了。如果負載均衡可能將這個會話的請求分配到不同的後臺服務端上,這肯定是不合適的,必須通過多個backend共享這些資料,效率肯定會很低下,最簡單的情況是保證會話一致性——相同的會話每次請求都會被分配到同一個backend上去。

  • 當客戶端第一次訪問伺服器時,負載均衡器按照某種均衡演算法將請求轉發到後端伺服器,後端伺服器在第一次響應某客戶端時,會在其頭部新增Cookie,之後客戶端的每次訪問都會帶有這個Cookie值,負載均衡器按照Cookie值分配給相應的伺服器。
  • 一致性雜湊演算法,通過一致性雜湊演算法實現當某一臺伺服器宕機或者下線時,最小影響伺服器叢集。

6.參考文件