1. 程式人生 > >【轉載】負載均衡Load Balance學習

【轉載】負載均衡Load Balance學習

轉載於 https://www.cnblogs.com/LittleHann/p/3963255.html

 

目錄

1. 負載均衡簡介
2. 負載均衡演算法
3. Nginx負載均衡排程演算法原始碼調研

 

1. 負載均衡簡介

0x1: 負載均衡是什麼

負載均衡是一種技術架構方法,它並不是具體指哪一種技術,也正是因為這樣,負載均衡被運用在了很多的領域

嚴格來說,負載平衡(Load balancing)是一種計算機網路技術,用來在多個計算機(計算機叢集)、網路連線、CPU、磁碟驅動器或其他資源中分配負載,以達到最佳化資源使用、最大化吞吐率、最小化響應時間、同時避免過載的目的,使用帶有負載平衡的多個伺服器元件,取代單一的元件,可以通過冗餘提高可靠性。負載平衡服務通常是由專用軟體和硬體來完成

負載平衡最重要的一個應用是利用多臺伺服器提供單一服務,這種方案有時也稱之為"伺服器農場"。通常,負載平衡主要應用於

複製程式碼
1. Web網站
2. 大型的Internet Relay Chat網路
3. 高流量的檔案下載網站
4. NNTP(Network News Transfer Protocol)服務
5. DNS服務
6. 海量SOCKET、HTTP長連線(常見於頁遊、MSN聊天軟體、APP等場景中)
7. 資料庫連線、查詢服務(稱之為資料庫負載平衡器)
複製程式碼

對於網際網路服務,負載平衡器通常是一個軟體程式,這個程式偵聽一個外部埠,網際網路使用者可以通過這個埠來訪問服務,而作為負載平衡器的軟體會將使用者的請求轉發給後臺內網伺服器,內網伺服器將請求的響應返回給負載平衡器,負載平衡器再將響應傳送到使用者,這樣就向網際網路使用者隱藏了內網結構,阻止了使用者直接訪問後臺(內網)伺服器,使得伺服器更加安全,可以阻止對核心網路棧和執行在其它埠服務的攻擊
當所有後臺伺服器出現故障時,有些負載平衡器會提供一些特殊的功能來處理這種情況。例如轉發請求到一個備用的負載平衡器、顯示一條關於服務中斷的訊息等。負載平衡器使得IT團隊可以顯著提高容錯能力。它可以自動提供大量的容量以處理任何應用程式流量的增加或減少

0x2: 負載均衡的類別

我們知道,負載均衡是一種架構思想,它在很多業務場景下都有得到運用,大體上,負載均衡有如下幾種

複製程式碼
1. 面向應用層的負載均衡器
    1.1 WEB訪問
        1) Apache模組mod_proxy(提供代理伺服器功能)
        http://www.php100.com/manual/apache2/mod/mod_proxy.html
        此模組實現了Apache的代理/閘道器。它實現了以下規範的代理:
            1.1) AJP13(Apache JServe Protocol v1.3): mod_proxy_ajp
            1.2) FTP: mod_proxy_ftp
            1.3) CONNECT(用於SSL): mod_proxy_connect
            1.4) HTTP/0.9: mod_proxy_http
            1.5) HTTP/1.0: mod_proxy_http
            1.6) HTTP/1.1: mod_proxy_http
        此模組經配置後可用上述或其它協議連線其它代理模組
        2) Apache Web伺服器的mod_proxy_balancer擴充套件(提供負載均衡功能)
        在使用基於Apache的負載均衡配置的時候,需要同時開啟mod_proxy、mod_proxy_balancer、mod_proxy_xx這些模組
        
        3) 基於反向代理的Nginx負載均衡器
        http://blog.sina.com.cn/s/blog_9c3ba23d01010rof.html

        4) Varnish負載均衡
        http://network.51cto.com/art/201005/198191.htm

        5) Pound反向代理和負載均衡器

        6) 基於URL Rewrite的負載均衡器

    1.2 DNS查詢
        1) 基於DNS輪詢的負載均衡: 
        用於將CLINET的DNS解析請求按照一定的演算法均衡地分配到一個IP叢集中,針對WEB URL訪問提供負載均衡,即將一個域名解析到多個IP地址,這種方法可能存在的問題是,輪叫DNS方法的排程粒度是基於每個域名伺服器的,
域名伺服器對域名解析的快取會妨礙輪叫解析域名生效,這會導致伺服器間負載的不平衡 2. 面向傳輸層的負載均衡器 1) HAProxy HAProxy提供高可用性、負載均衡以及基於TCP和HTTP應用的代理,支援虛擬主機 3. 面向網路層的負載均衡器 3.1 LVS(Linux Virtual Server) 1) Virtual Server via Network Address Translation(VS/NAT) 通過網路地址轉換,排程器重寫請求報文的目標地址,根據預設的排程演算法,將請求分派給後端的真實伺服器;真實伺服器的響應報文通過排程器時,報文的源地址被重寫,再返回給客戶,完成整個負載排程過程。 2) Virtual Server via IP Tunneling(VS/TUN) 採用NAT技術時,由於請求和響應報文都必須經過排程器地址重寫,當客戶請求越來越多時,排程器的處理能力將成為瓶頸。為了解決這個問題,排程器把請求報文通過IP隧道轉發至真實伺服器,而真實伺服器將響應直接返回給
客戶,所以排程器只處理請求報文。由於一般網路服務應答比請求報文大許多,採用VS/TUN技術後,集群系統的最大吞吐量可以提高10倍 3) Virtual Server via Direct Routing(VS/DR) VS/DR通過改寫請求報文的MAC地址,將請求傳送到真實伺服器,而真實伺服器將響應直接返回給客戶。同VS/TUN技術一樣,VS/DR技術可極大地提高集群系統的伸縮性。這種方法沒有IP隧道的開銷,對叢集中的真實伺服器也
沒有必須支援IP隧道協議的要求,但是要求排程器與真實伺服器都有一塊網絡卡連在同一物理網段上 3. 多層負載均衡: 高效能系統通常會使用 對於一個多層次架構體系,在負載均衡器或網路分發器後面有兩種設計,術語稱之為Bowties和Stovepipes 1) Stovepipe 事務是從頂部分發的,然後從一個固定通道通過一系列硬體和軟體裝置,到達最終目的地 2) Bowties 在每一層中事務處理有多條路徑可供選擇 在事務處理的網路結構中可能會是Stovepipes,也可以是Bowties,或者根據每一層的實際需求採用雜貨構架 4. 跨語言異構系統間通訊負載均衡排程器 1) Gearman 使用Gearman將合適的計算任務分發給多臺計算機,如此大量的任務就可以更快的完成了 http://baike.baidu.com/view/2926980.htm?fr=aladdin http://blog.chinaunix.net/uid-20357359-id-1963682.html 對於Gearman(齒輪工)的技術架構,我們需要重點理解幾點 1.1) Gearman是一種API級的跨系統通訊方式,這種方式和REST、RPC相比提高了耦合度 1.2) 從某種程度上說,Gearman和資料庫技術中的儲存過程的思想很類似,通過在預處理階段實現後端的實現邏輯,並向前端暴露出API級的呼叫介面,這種方式提供了API的準確性、定製性、安全性 1.3) client、job schedule server、worker之間的API通訊是基於網路IO實現的
複製程式碼

0x3: 負載均衡的特性

當前,負載均衡器有各種各樣的"工作排程演算法"(用於決定將前端使用者請求傳送到哪一個後臺伺服器),這是負載均衡最核心的思想,負載均衡器相當於是在原始網路鏈路上的中間插入一個"Proxy",這個Proxy可以位於網路架構上的任何一個位置,用於將原來的"1:N"的效能瓶頸關係轉化為"N:N"關係,只要是遵循了這個思想的架構和方案,都可以說是實現了負載均衡的架構,從某種程度上來說,負載均衡器就是反向代理伺服器,負載轉發並重建立原始連線

不論是軟體負載均衡器,還是硬體負載均衡器都有一系列的特性

複製程式碼
1. 不對稱負載調節
可以對後臺伺服器設定權重因子,權重因子用於控制伺服器的請求處理量,進而控制伺服器的負載。當後臺伺服器的處理能力不是等同的時候,這是一種控制伺服器負載的簡單方法

2. 優先啟動
當出現故障的伺服器達到某個閥值,或者伺服器負載過高時,備用伺服器必需可以及時上線提供服務

3. SSL截斷和加速(SSL解除安裝)
依賴伺服器負載,處理加密資料或者通過SSL進行的授權請求會消耗Web伺服器的大量CPU,隨著需求增加使用者會明顯感覺到響應時間變長。為了消除Web伺服器上這部分(處理加密)負載,負載均衡器可能會將SSL通訊截斷在負載均衡器上。
有些硬體負載均衡器上包含有專門用於處理SSL的硬體。當負載均衡器截斷SSL連線請求,再將使用者請求轉發給後臺前將HTTPS變為HTTP。只要負載均衡器不超載,這個特性不會影響使用者體驗。 這種方法的負面效應是,由於所有SSL都由負載均衡器一臺裝置來處理,它會導致負載均衡器成為負載均衡體系的一個瓶頸。如果不使用這個特性,SSL請求將會分發給各個Web伺服器處理。是否採用這一特性,需要分析比較兩者的資金投
入情況,含有處理SSL特殊硬體的負載均衡器通常價格高昂,而Web伺服器一般比較廉價。增加少量的Web伺服器的花費可能明顯比升級負載均衡器要少。另外,一些伺服器廠商如Oracle/Sun也開始在它們的CPU中加入加密加速模組,
例如T2000。在負載均衡器上截斷SSL的另一個優點是允許負載均衡器可以對基於HTTPS請求資料進行負載均衡或內容交換 4. DDOS攻擊防護 負載均衡器可以提供例如SYN cookies特性和延時繫結(在TCP握手完成之前,後臺伺服器不會與使用者通訊)來減緩SYN flook攻擊,and generally offload work from the servers to a more efficient platform 5. HTTP壓縮 使用gzip壓縮HTTP資料,以減少網路上的資料傳輸量。對於響應時間較長,傳輸距離較遠的使用者,這一特性對於縮短響應時間效果明顯。這個特性會要求更多一些的負載均衡器CPU,這一功能也可以由Web伺服器來完成 6. TCP offload 其主要思想是一樣的,通常每個使用者的每個請求都會使用一個不同的TCP連線,這個特性利用HTTP/1.1將來自多個使用者的多個請求合併為單個TCP socket再轉發給後臺伺服器,即我們常說的TCP快取組包技術 7. TCP緩衝 負載均衡器可以暫存後臺伺服器對客戶的響應資料,再將它們轉發給那些響應時間較長網速較慢的客戶,如此後臺Web伺服器就可以釋放相應的執行緒去處理其它任務如直接整個響應資料直接傳送給網速較快的使用者,這可以從一定程度上解
決"HTTP SLOW ATTACK"慢速攻擊 8. 後臺伺服器直接響應使用者(Direct Server Return) 這是不對稱負載分佈的一項功能,在不對稱負載分佈中請求和迴應通過不同的網路路徑 9. 伺服器健康檢查 負載均衡器可以檢查後臺伺服器應用層的健康狀況並從伺服器池中移除那些出現故障的伺服器 10. HTTP快取 負載均衡器可以儲存"靜態內容"(相比於靜態內容,動態指令碼的執行的快取需要慎重考慮),當用戶請求它們時可以直接響應使用者而不必再向後臺伺服器請求 11. 內容過濾 負載均衡器可以按要求修改通過它的資料 12. HTTP安全 負載均衡器可以隱藏HTTP出錯頁面,刪除HTTP響應頭中的伺服器標示資訊,加密cookies以防止使用者修改 13. 優先佇列 也可稱之為流量控制。它可以對不同的內容設定不同的優先順序 14. Content-aware switching(內容感知開關) 負載均衡器可以基於使用者請求的URL傳送請求到不同的後臺伺服器,無論內容是加密(HTTPS)還是沒有加密(HTTP) 15. 使用者授權 對來自不同身份驗證源的使用者進行驗證,然後再允許他們訪問一個網站 16. 可程式設計的流量控制 負載均衡器允許使用指令碼程式設計來定製負載均衡方法,任意的流量控制以及其它功能 17. 防火牆功能 由於安全的原因,不允許使用者直接訪問後臺伺服器。防火牆是由一系列規則構成,它們決定著哪些請求可以通過一個介面而哪些不被允許。 提供入侵阻止功能。在防火牆保障網路層/傳輸層安全的基礎上,提供應用層安全防範
複製程式碼

 

2. 負載均衡演算法

最簡單的是隨機選擇和輪詢。更為高階的負載均衡器會考慮其它更多的相關因素,如後臺伺服器的負載,響應時間,執行狀態,活動連線數,地理位置,處理能力,或最近分配的流量

複製程式碼
1. 針對WEB訪問請求的負載均衡排程策略(反向代理的網路連線方式)
    1) RR(Round Robin)
    每個請求按時間順序逐一分配到不同的後端伺服器,如果後端伺服器down掉,能自動剔除,在實現RR演算法的時候,還需要根據其他的因素: max_fails、fail_timeout、weight等因素進行綜合評估
    
    2) WEIGHT(權重) 
    指定輪詢機率,WEIGHT和訪問比率成正比,用於後端伺服器效能不均的情況

    3) IP_HASH 
    每個請求按訪問IP的Hash結果分配,這樣每個訪客固定訪問一個後端伺服器,可以解決"負載叢集SESSION同步"的問題(因為Hash具有結果一致性)

    4) Fair(第三方) 
    按後端伺服器的響應時間來分配請求,響應時間短的優先分配 

    5) URL_HASH(第三方)
    按訪問URL的Hash結果來分配請求,使每個URL定向到同一個後端伺服器,後端伺服器為快取時比較有效

2. 針對網路IP層訪問請求的負載均衡排程策略(面向SOCKET的IP負載均衡)
    1) 輪叫(Round Robin)
    排程器通過"輪叫"排程演算法將外部請求按順序輪流分配到叢集中的真實伺服器上,它均等地對待每一臺伺服器,而不管伺服器上實際的連線數和系統負載

    2) 加權輪叫(Weighted Round Robin)
    排程器通過"加權輪叫"排程演算法根據真實伺服器的不同處理能力來排程訪問請求。這樣可以保證處理能力強的伺服器處理更多的訪問流量。排程器可以自動問詢真實伺服器的負載情況,並動態地調整其權值。

    3) 最少連結(Least Connections)
    排程器通過"最少連線"排程演算法動態地將網路請求排程到已建立的連結數最少的伺服器上。如果集群系統的真實伺服器具有相近的系統性能,採用"最小連線"排程演算法可以較好地均衡負載。

    4) 加權最少連結(Weighted Least Connections)
    在集群系統中的伺服器效能差異較大的情況下,排程器採用"加權最少連結"排程演算法優化負載均衡效能,具有較高權值的伺服器將承受較大比例的活動連線負載。排程器可以自動問詢真實伺服器的負載情況,並動態地調整其權值。

    5) 基於區域性性的最少連結(Locality-Based Least Connections)
    "基於區域性性的最少連結" 排程演算法是針對目標IP地址的負載均衡,目前主要用於Cache集群系統。該演算法根據請求的目標IP地址找出該目標IP地址最近使用的伺服器,若該伺服器 是可用的且沒有超載,將請求傳送到該伺服器;若服
務器不存在,或者該伺服器超載且有伺服器處於一半的工作負載,則用"最少連結"的原則選出一個可用的伺服器,將請求傳送到該伺服器。 6) 帶複製的基於區域性性最少連結(Locality-Based Least Connections with Replication) "帶複製的基於區域性性最少連結"排程演算法也是針對目標IP地址的負載均衡,目前主要用於Cache集群系統。它與LBLC演算法的不同之處是它要維護從一個目標IP地址到一組伺服器的對映,而LBLC演算法維護從一個目標IP地址到一臺服務
器的對映。該演算法根據請求的目標IP地址找出該目標IP地址對應的服務 器組,按"最小連線"原則從伺服器組中選出一臺伺服器,若伺服器沒有超載,將請求傳送到該伺服器,若伺服器超載;則按"最小連線"原則從這個叢集中選出一臺服
務器,將該伺服器加入到伺服器組中,將請求傳送到該伺服器。同時,當該伺服器組有一段時間沒有被修改,將最忙的伺服器從伺服器組中刪除,以降低複製的 程度。 7) 目標地址雜湊(Destination Hashing) "目標地址雜湊"排程演算法根據請求的目標IP地址,作為雜湊鍵(Hash Key)從靜態分配的散列表找出對應的伺服器,若該伺服器是可用的且未超載,將請求傳送到該伺服器,否則返回空。 8) 源地址雜湊(Source Hashing) "源地址雜湊"排程演算法根據請求的源IP地址,作為雜湊鍵(Hash Key)從靜態分配的散列表找出對應的伺服器,若該伺服器是可用的且未超載,將請求傳送到該伺服器,否則返回空
複製程式碼

Relevant Link:

http://blog.sina.com.cn/s/blog_9c3ba23d01010rof.html
http://zh.wikipedia.org/wiki/%E8%B4%9F%E8%BD%BD%E5%9D%87%E8%A1%A1_(%E8%AE%A1%E7%AE%97%E6%9C%BA)
http://www.linuxvirtualserver.org/zh/lvs1.html#7

 

3. Nginx負載均衡排程演算法原始碼調研 

nginx 的負載均衡策略可以劃分為兩大類:內建策略和擴充套件策略。內建策略包含加權輪詢和ip hash,在預設情況下這兩種策略會編譯進nginx核心,只需在nginx配置中指明引數即可。擴充套件策略有很多,如fair、通用hash、 consistent hash等,預設不編譯進nginx核心,是第三方模組

在開始學習排程演算法之前,我們需要先了解一下Nginx排程演算法所涉及到的資料結構,Nginx的負載均衡是根據配置檔案進行的,所以Nginx在初始化的時候需要從配置檔案中讀取配置並儲存到相應的結構體中

1. struct ngx_conf_t

\nginx-1.0.14_comment-master\src\core\ngx_core.h

typedef struct ngx_conf_s        ngx_conf_t;

\nginx-1.0.14_comment-master\src\core\ngx_conf_file.h

複製程式碼
struct ngx_conf_s 
{
    char                 *name;  //沒有使用
    ngx_array_t          *args;  //指令的引數

    ngx_cycle_t          *cycle; //指向系統引數,在系統整個執行過程中,
                                 //需要使用的一些引數、資源需要統一的管理
    ngx_pool_t           *pool;  //記憶體池
    ngx_pool_t           *temp_pool; //分配臨時資料空間的記憶體池
    ngx_conf_file_t      *conf_file; //配置檔案的資訊
    ngx_log_t            *log; //日誌

    void                 *ctx;  //模組的配置資訊
    ngx_uint_t            module_type; //當前指令的型別
    ngx_uint_t            cmd_type; //命令的型別

    ngx_conf_handler_pt   handler; //指令處理函式,有自己行為的在這裡實現
    char                 *handler_conf; //指令處理函式的配置資訊
};
複製程式碼

2. struct ngx_http_upstream_srv_conf_t

\nginx-1.0.14_comment-master\src\http\ngx_http_upstream.h

typedef struct ngx_http_upstream_srv_conf_s  ngx_http_upstream_srv_conf_t;

\nginx-1.0.14_comment-master\src\http\ngx_http_upstream.h

複製程式碼
struct ngx_http_upstream_srv_conf_s 
{
    ngx_http_upstream_peer_t         peer;
    void                           **srv_conf;  // 在ngx_http_upstream()函式中被設定,指向的是本層的srv_conf  

    ngx_array_t                     *servers;   // ngx_http_upstream_server_t

    ngx_uint_t                       flags;     // 呼叫函式時ngx_http_upstream_add() 指定的標記  
    ngx_str_t                        host;      // 在函式 ngx_http_upstream_add()中設定(e.g. upstream backend中的backend)
    u_char                          *file_name; // "/usr/local/nginx/conf/nginx.conf"  
    ngx_uint_t                       line;      // proxy在配置檔案中的行號  
    in_port_t                        port;      // 使用的埠號,ngx_http_upstream_add()函式中新增, 指向ngx_url_t-->port,通常在函式ngx_parse_inet_url()中解析
    in_port_t                        default_port;  //預設使用的埠號(ngx_http_upstream_add()函式中新增, 指向ngx_url_t-->default_port)
};
複製程式碼

3. struct ngx_http_upstream_peer_t

\nginx-1.0.14_comment-master\src\http\ngx_http_upstream.h

複製程式碼
typedef struct 
{
    //使用負載均衡的型別,預設採用ngx_http_upstream_init_round_robin()
    ngx_http_upstream_init_pt        init_upstream;

    //使用的負載均衡型別的初始化函式 
    ngx_http_upstream_init_peer_pt   init;

    //us->peer.data = peers; 指向的是 ngx_http_upstream_rr_peers_t(函式 ngx_http_upstream_init_round_robin()中設定)
    void                            *data;
} ngx_http_upstream_peer_t;
複製程式碼

ngx_http_upstream_init_peer_pt、ngx_http_upstream_init_pt 是函式指標型別

\nginx-1.0.14_comment-master\src\http\ngx_http_upstream.h

typedef ngx_int_t (*ngx_http_upstream_init_pt)(ngx_conf_t *cf, ngx_http_upstream_srv_conf_t *us);
typedef ngx_int_t (*ngx_http_upstream_init_peer_pt)(ngx_http_request_t *r, ngx_http_upstream_srv_conf_t *us);

4. struct ngx_array_t 

\nginx-1.0.14_comment-master\src\core\ngx_core.h

typedef struct ngx_array_s       ngx_array_t;

\nginx-1.0.14_comment-master\src\core\ngx_array.h

複製程式碼
// 動態陣列
struct ngx_array_s 
{
    // elts指向陣列的首地址
    void        *elts; 
    // nelts是陣列中已經使用的元素個數
    ngx_uint_t   nelts; 
    // 每個陣列元素佔用的記憶體大小
    size_t       size;  
    // 當前陣列中能夠容納元素個數的總大小
    ngx_uint_t   nalloc; 
    // 記憶體池物件
    ngx_pool_t  *pool;  
};
複製程式碼

5. struct ngx_http_upstream_server_t

\nginx-1.0.14_comment-master\src\http\ngx_http_upstream.h
需要注意的是,配置檔案中出現的引數只能和某些策略配合使用,所以如果發現某引數沒有生效,則應該檢查這一點。在配置解析的過程中,這些選項設定都被轉換為Nginx內對於的變數值,對應的結構體ngx_http_upstream_server_t如下

複製程式碼
typedef struct 
{
    ngx_addr_t                      *addrs;         //指向儲存IP地址的陣列的指標,host資訊(對應的是 ngx_url_t->addrs )  
    ngx_uint_t                       naddrs;        //與第一個引數配合使用,陣列元素個數(對應的是 ngx_url_t->naddrs )  
    ngx_uint_t                       weight;
    ngx_uint_t                       max_fails;
    time_t                           fail_timeout;

    unsigned                         down:1;
    unsigned                         backup:1;
} ngx_http_upstream_server_t;
複製程式碼

6. struct ngx_http_upstream_rr_peer_t

\nginx-1.0.14_comment-master\src\http\ngx_http_upstream_round_robin.h

複製程式碼
typedef struct 
{
    struct sockaddr                *sockaddr;        //後端伺服器地址  
    socklen_t                       socklen;        //後端伺服器地址長度
    ngx_str_t                       name;        //後端名稱  
    ngx_str_t                       server;

    ngx_int_t                       current_weight;    //當前權重,nginx會在執行過程中調整此權重  
    ngx_int_t                       effective_weight;    //配置的權重
    ngx_int_t                       weight;        //current_weight是執行時的動態權值

    ngx_uint_t                      fails;        //已嘗試失敗次數  
    time_t                          accessed;        //檢測失敗時間,用於計算超時  
    time_t                          checked;

    ngx_uint_t                      max_fails;        //最大失敗次數  
    time_t                          fail_timeout;    //多長時間內出現max_fails次失敗便認為後端down掉了  

    ngx_uint_t                      down;          /* unsigned  down:1; */

#if (NGX_HTTP_SSL)
    ngx_ssl_session_t              *ssl_session;   /* local to a process */
#endif
} ngx_http_upstream_rr_peer_t;
複製程式碼

列表最前面需要帶有一些head資訊,用結構體ngx_http_upstream_rr_peers_t與之對應

7struct ngx_http_upstream_rr_peers_t

\nginx-1.0.14_comment-master\src\http\ngx_http_upstream_round_robin.h

複製程式碼
typedef struct ngx_http_upstream_rr_peers_s  ngx_http_upstream_rr_peers_t;

struct ngx_http_upstream_rr_peers_s 
{
    ngx_uint_t                      single;         // unsigned  single:1;  
    ngx_uint_t                      number;         // 佇列中伺服器數量 
    ngx_uint_t                      last_cached;

 /* ngx_mutex_t                    *mutex; */
    ngx_connection_t              **cached;

    ngx_str_t                      *name;

    ngx_http_upstream_rr_peers_t   *next;           // 後備伺服器列表掛載在這個欄位下  

    ngx_http_upstream_rr_peer_t     peer[1];
};
複製程式碼

0x1: 帶權重的RR排程策略

圖片很大,可以另存到本地看

可以看到,Weight RR加權輪叫的流程簡單來說可以概括為: 讀取配置檔案->儲存配置檔案中的指定選項->建立後端伺服器叢集的資料結構,併為止設定引數->根據權重動態計算公式在每次請求到達時對每個後端伺服器的權值進行動態更新,每次都選出一個最優權值伺服器進行排程

0x2: Nginx的負載均衡權重動態調整演算法

在ngx_http_upstream_get_peer()中傳入的引數是一個ngx_http_upstream_rr_peer_t結構體

複製程式碼
typedef struct 
{
    ...
    /*
    1. 當前權重,nginx會在執行過程中調整此權重  
    current_weight相當於重量,是執行時的動態權值,它的變化基於effective_weight。但是
        1) effective_weight在其對應的peer服務異常時,會被調低
        2) 當服務恢復正常時,effective_weight會逐漸恢復到實際值(配置的weight)
    */
    ngx_int_t    current_weight;         

    /*
    2. 配置的權重
    effective_weight相當於質量(來源於配置的weight),反應了本質
    */
    ngx_int_t    effective_weight;     
    ngx_int_t    weight;            //current_weight是執行時的動態權值
    ...
 } ngx_http_upstream_rr_peer_t;
複製程式碼

weight、effective_weight都是初始化為配置項中的weight值。current_weight初始化為0

1. weight的值在整個執行過程中不發生變化
2. total變數記錄了針對一個服務列表的一次輪詢過程中輪詢到的所有服務的effective_weight總和。在每一次針對服務列表的輪詢之前會置為為0
3. 遍歷服務列表的過程中,每遍歷到一個服務
    1) 會在該服務的current_weight上加上其對應的effective_weight。這個是累加
    2) 如果對統一的服務列表進行另一次輪詢,那麼會在前面計算的current_weight的基礎之上再加上effective_weight
4. 輪詢策略是取current_weight最大的伺服器。每次取到後端服務(用best表示)後,都會把該物件peer的current_weight減去total的值。因為該服務剛被選中過,因此要降低權值

關於effective_weight的變化,有兩處

複製程式碼
1. ngx_http_upstream_get_peer()中
//服務正常,effective_weight 逐漸恢復正常      
if (peer->effective_weight < peer->weight) 
{  
    peer->effective_weight++;  
}

2. 另一處是在釋放後端服務的函式ngx_http_upstream_free_round_robin_peer中
if (peer->max_fails) 
{  
     //服務發生異常時,調低effective_weight  
    peer->effective_weight -= peer->weight / peer->max_fails;  
}
複製程式碼

權重高的會優先被選中,而且被選中的頻率也更高。權重低的也會由於權重逐漸增長獲得被選中的機會

0x3: IP_HASH排程策略

IP_hash和RR 的策略有兩點不同在於: 當一個客戶請求到nginx後

1. Nginx如何選擇一個最初的server
2. 以及當前選擇的server不能提供服務時,如何選擇下一個server

在IP_hash策略中

複製程式碼
1. 它選擇最初的server的方法是根據請求客戶端的IP計算出一個雜湊值,再根據雜湊值選擇後臺的伺服器,由IP計算雜湊值的演算法如下
//其中公式中hash初始值為89,iphp->addr[i]表示客戶端的IP,通過三次雜湊計算得出一個IP的雜湊值
for (i = 0; i < 3; i++) 
{
      hash = (hash * 113 + iphp->addr[i]) % 6271;
}
 
2. 在選擇下一個server時,ip_hash的選擇策略是這樣的:
它在上一次雜湊值的基礎上,再次雜湊,就會得到一個全新的雜湊值,再根據雜湊值選擇另外一個後臺的伺服器。雜湊演算法仍然是
for (i = 0; i < 3; i++) 
{
      hash = (hash * 113 + iphp->addr[i]) % 6271;
}
 
3. 在這種ip_hash策略
    1) 如果一個後臺伺服器不能提供提服務(連線超時或讀超時),該伺服器的失敗次數就會加一,當一個伺服器的失敗次數達到max_fails所設定的值,就會在fail_timeout所設定的時間段內不能對外提供服務,這點和RR是一致的
    2) 如果當前server不能提供服務,就會根據當前的雜湊值再雜湊出一個新雜湊值,選擇另一個伺服器繼續嘗試,嘗試的最大次是upstream中server的個數,如果server的個數超過20,也就是要最大嘗試次數在20次以上,當嘗試
次數達到20次,仍然找不到一個合適的伺服器,ip_hah策略不再嘗試ip雜湊值來選擇server, 而在剩餘的嘗試中,它會轉而使用RR的策略,使用輪循的方法,選擇新的server。 4. 除了以上部分不同外,IP_hash的其餘部分和RR完全一樣,因為它的其餘部分功能的實現都是通過呼叫RR中的函式
複製程式碼

Relevant Link:

複製程式碼
http://www.cnblogs.com/yjf512/archive/2012/06/13/2548515.html
https://github.com/jianfengye/nginx-1.0.14_comment
http://blog.csdn.net/gzh0222/article/details/7996835
http://blog.csdn.net/livelylittlefish/article/details/6571497
http://blog.sina.com.cn/s/blog_9c3ba23d01010rof.html
http://blog.csdn.net/xiajun07061225/article/details/9318871
http://baidutech.blog.51cto.com/4114344/1033718
《nginx模組開發與架構解析》
複製程式碼