負載均衡Load Balance學習


目錄

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與之對應

7. struct 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模塊開發與架構解析》

 

Copyright (c) 2014 LittleHann All rights reserved

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM