Nginx 做負載均衡的幾種輪詢策略

SanDunbabin 8年前發布 | 24K 次閱讀 Web服務器

來自: http://www.educity.cn/wenda/143095.html


Nginx 做負載均衡的幾種輪詢策略
轉載:

一、nginx的upstream目前支持負載均衡方式的分配
1、RR(默認)
每個請求按時間順序逐一分配到不同的后端服務器,如果后端服務器down掉,能自動剔除。
例如:
upstream tomcats {
server 10.1.1.107:88  max_fails=3 fail_timeout=3s weight=9;
server 10.1.1.132:80  max_fails=3 fail_timeout=3s weight=9;
}

2、ip_hash
每個請求按訪問ip的hash結果分配,這樣每個訪客固定訪問一個后端服務器,可以解決session的問題。
例如:
upstream tomcats {
ip_hash;
server 10.1.1.107:88;
server 10.1.1.132:80;
}

3、fair(第三方)
按后端服務器的響應時間來分配請求,響應時間短的優先分配。

4、url_hash(第三方)
按訪問url的hash結果來分配請求,使每個url定向到同一個后端服務器,后端服務器為緩存時比較有效。

下面,我們針對RR和ip_hash的負載均衡策略進行分析。因為每一種負載均衡策略都是在upstream的框架中使用,upstream控制總的工作流程,負載均衡策略僅僅提供選擇或釋放server的函數,所以,我們在分析RR時結合upstream(ngx_)。 ip_hash大部分內容與RR一致,只是重新實現RR中的ngx_http_upstream_get_peer函數。

二、RR策略
RR機制分為三個部分:初始化upstream,獲取一個可用的后臺服務器和釋放后臺服務器。
以下分析以此配置為例:
upstream backend  {
          server  A  max_fails=3 fail_timeout=4s weight=9;
          server  B  max_fails=3 fail_timeout=4s weight=9;
          server  C  max_fails=3 fail_timeout=4s weight=9;
          server  D  backup;
          Server  E  backup;
}

2.1 初始化upstream
   
    對于例子中的upstream backend來說,

首先初始化各個server, 除了設置IP和端口號外,還要設置如下置weight,current_weight,max_fails和fail_timeout。其中max_fails和fail_timeout 這兩個參數是組合使用的,表示server 如果失敗次數達到max_fails 次,并保持fail_timeout秒之內該服務器不能被訪問。
   對于serverA來說,設置如下
serverA.weight =9;
serverA.current_weight = 9;  //初始值等于配置文件中的weight.
serverA.max_fails = 3;
serverA.fail_timeout = 4;

   接著,創建兩個server類型(在下文介紹中,server類型等同于peer類型,都是用來指明存儲upstream中一個server的信息)的數組,peers和backup,分別存儲正常的輪循server和備用server. 并且,按照數組中各個server的weight值的大小,由高到底排序。
   本例中,在數組peers中存儲serverA、serverB和 serverC, 并記錄server的總個數peers->number=3; 在數組backup中存儲serverD和 serverE, 并記錄server的總個數backup->number=2;

   最后,設置upstream中各個變量的值。
   rrp  表示當前要輪循的server數組,初始設置為Upstream->rrp = peers.
   tries 表示嘗試的次數,當嘗試一個server失敗后,tries的值就會減一。初始設置為peers的總個數。
   Next 表示當peers數組中server都失敗,不能提供服務了,通過upstream->next,切換到back數組中選擇server.

2.2 具體的RR策略

2.2.1 ) 選擇最初要輪循的server,  把它給rrp->current變量,跳轉到2.2.2
   當一個客戶端請求到達nginx后,nginx就會在upstream的peers 數組中挑選一個current_weight最大的server作為當前請求最初要輪循的server. 在peers數組中選取current_weight最大的算法如下:
    由于peers數組中的server是按照weight值的大小排序好的。
    它是通過雙重循環,滿足下列條件后,
if (peer[n].current_weight * 1000 / peer[i].current_weight > peer[n].weight * 1000 / peer[i].weight)  //peer[i].current_weight不為0
     并且該server的current_weight大于0,就選擇sever  n, 把編號n賦給rrp->current,成功返回。
     如果當upstream的peers 數組中的所有server的current_weight都為零時,立即無條件地把所有server的current_weight設置為初始值。for (i = 0; i < peers->number; i++) {
            peer[i].current_weight = peer[i].weight;
}
    然后,當所有server的current_weight設置為初始值后,重新查找peers 數組中current_weight最大的server。把編號賦給rrp->current,返回。

2.2.2 判斷當前rrp->current所指向的server是否有效,如果無效,就會讓rrp->current++,判斷peers數組中下一個server,是否有效。至到找到有效的server為止. 跳轉到2.2.3; 否則跳轉到2.2.2.1

     判斷server 是否有效的方法是:
         1)如果server的失敗次數(peers->peer[i].fails)沒有達到了max_fails所設置的最大失敗次數,則該server是有效的。
         2)如果server已經達到了max_fails所設置的最大失敗次數,從這一時刻開始算起,在fail_timeout 所設置的時間段內, server是無效的。
         3)當server的失敗次數(peers->peer[i].fails)為最大的失敗次數,當距離現在的時間超過了fail_timeout 所設置的時間段, 則令peers->peer[i].fails =0,使得該server重新有效。

      2.2.2.1如果peers中所有的server都是無效的; 就會嘗試去backup的數組中找一個有效的server, 如果找到, 跳轉到2.2.3; 如果仍然找不到,表示此時upstream中無server可以使用。就會清空所有peers數組中所有的失敗次數的記錄,使所有server都變成了有效。這樣做的目的是為了防止下次再有請求訪問時,仍找不到一個有效的server.
for (i = 0; i < peers->number; i++) {
        peers->peer[i].fails = 0;
}
并返回錯誤碼給nginx,  nginx得到此錯誤碼后,就不再向后臺server發請求,而是在nginx的錯誤日志中輸出“no live upstreams while connecting to upstream”的記錄(這就是no live產生的真正原因),并直接返回給請求的客戶端一個502的錯誤。

2.2.3 當找到一個有效的server后,令該server的current_weight減一,然后,nginx就會嘗試與該server建立連接。如果成功建立連接,跳轉到2.2.4; 否則 跳轉到2.2..3.1
2.2..3.1 如果nginx在等待了proxy_connect_timeout所設置的時間段后(如3秒),連接仍然沒有建立成功,nginx就在錯誤日志中輸出“upstream timed out (110: Connection timed out) while connecting to upstream”的記錄(這就是 timed out(連接超時)產生的真正原因).
     2.2.3 .2 接著,讓當前server的失敗次數加一(peer->fails++; 如果該server最大失敗次達到最大失敗次數,將在一段時間內該server是無效的),如果當前nginx與后臺服務器的嘗試次數沒有達到upstream中server的總個數,重新跳轉到2.2.2, 輪循下一個server, 繼續嘗試。如果達到最大嘗試次數,就表示uptream中所有的server都嘗試了一遍,沒有server可以提供服務,返回一個504的錯誤給客戶端。

    2.2.4 當nginx與server建立連接成功后,如果server響應請求,把處理結果返回給nginx,
跳轉到2.2.5; 否則跳轉到2.2.4.1
    2.2.4.1 如果nginx在等待了proxy_read_timeout所設置的時間段后(如30秒),server仍然沒有對nginx發送來的請求作出響應,nginx就在錯誤日志中輸出“upstream timed out (110: Connection timed out) while reading response header from upstream”的記錄(這就是 timed out(讀超時)產生的真正原因).
        2.2.4.2 接著,讓當前server的失敗次數加一(peer->fails++; 如果該server最大失敗次達到最大失敗次數,將在一段時間內該server是無效的),如果當前nginx與后臺服務器的嘗試次數沒有達到upstream中server的總個數,重新跳轉到2.2.2, 輪循下一個server, 繼續嘗試。如果達到最大嘗試次數,就表示uptream中所有的server都嘗試了一遍,沒有server可以提供服務,返回一個504的錯誤給客戶端。

2.2.5 Nginx收到后臺server傳送過來的結果后,就會返回給客戶端一個200的正確結果。這樣,nginx作為反向代理的功能也就完成了。



三、Ip_hash策略
3.1 Ip_hash和RR 的策略有兩點不同在于:

當一個客戶請求到nginx后,
1)nginx如何選擇一個最初的server,
2)以及當前選擇的server不能提供服務時,如何選擇下一個server.

3.2  RR策略回顧
從第二部分對RR的介紹中,我們知道:
當一個客戶請求到達后,RR策略是從upstream的所有server中選擇一個當前權重(current_weight)最大的server作為最初的server.
upstream的所有server是按照由高到低排序后存儲在一個peers數組中,當最初選擇的server不能提供服務時,RR策略就會選擇peers數組中的下一個元素作為當前server,繼續嘗試, 如果已經達到數組的最大元素,就會從第一個元素再輪循。

3.3 ip_hash策略介紹
   在ip_hash策略中,它選擇最初的server的方法是根據請求客戶端的IP計算出一個哈希值,再根據哈希值選擇后臺的服務器。
1)由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;
}

    在這種ip_hash策略,如果一個后臺服務器不能提供提服務(連接超時或讀超時),該服務器的失敗次數就會加一,當一個服務器的失敗次數達到max_fails所設置的值,就會在fail_timeout所設置的時間段內不能對外提供服務,這點和RR是一致的。
   如果當前server不能提供服務,就會根據當前的哈希值再哈希出一個新哈希值,選擇另一個服務器繼續嘗試,嘗試的最大次是upstream中server的個數,如果server的個數超過20,也就是要最大嘗試次數在20次以上,當嘗試次數達到20次,仍然找不到一個合適的服務器,ip_hah策略不再嘗試ip哈希值來選擇server, 而在剩余的嘗試中,它會轉而使用RR的策略,使用輪循的方法,選擇新的server。
3)除了以上部分不同外,IP_hash的其余部分和RR完全一樣,因為它的其余部分功能的實現都是通過調用RR中的函數。
4)IP_hash優勢是把同一個客戶IP的請求分配給同一個后臺服務器。

 本文由用戶 SanDunbabin 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!