nginx做為方向代理時,能夠為後端伺服器提供負載均衡的功能,其中加權輪詢策略使是其預設的負載均衡策略。
直觀上就是將來自客戶的請求按照每個伺服器的權值進行負載均衡(權值大的伺服器處理的請求也應該多)。那麼對於每次客戶的請求如何選取後端伺服器才能確保這種均衡呢?nginx採用加權輪詢策略時選取後端伺服器的核心程式碼是ngx_http_upstream_get_peer函式(位於ngx_http_upstream_round_robin.c中)。下面對這段程式碼進行分析。
- 程式碼說明:
(1) peer[n].weight:後端伺服器初始權重。
(2) peer[n].current_weight:後端伺服器當前權重,初始情況等於peer[n].weight。
(3) peers->number:後端伺服器的個數
(4) peers->peer[0]:一個陣列的第一個元素,這個陣列的每個元素對應一個後端伺服器。
(5) 一旦某個後端伺服器n被選中後,會在其他處理函式中執行peer[n].current_weight–。
(6) 程式碼18行乘以1000是為了避免浮點處理,所以直接報被除數放大1000倍,也就是間接把精度提升到小數點後三位,注意這裡是權值的比較,因此把兩邊權值都放大1000倍並不會影響最終的比較結果。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 |
static ngx_uint_t ngx_http_upstream_get_peer(ngx_http_upstream_rr_peers_t *peers) { ngx_uint_t i, n, reset = 0; ngx_http_upstream_rr_peer_t *peer; peer = &peers->peer[0]; //peer指向後端伺服器列表 for ( ;; ) { for (i = 0; i < peers->number; i++) { if (peer[i].current_weight <= 0) { continue; } n = i; //n為第一個current_weight大於0的伺服器下標 while (i < peers->number - 1) { i++; //i從n的下一個伺服器開始遍歷 if (peer[i].current_weight <= 0) { continue; } if (peer[n].current_weight * 1000 / peer[i].current_weight > peer[n].weight * 1000 / peer[i].weight) //選取後端伺服器的關鍵 { return n; } n = i; } if (peer[i].current_weight > 0) { n = i; } return n; } if (reset++) { //初始為0,所以第二次迴圈到此條件才成立,注意是後置自增。 return 0; } for (i = 0; i < peers->number; i++) { peer[i].current_weight = peer[i].weight; } } } |
- 分析:
18~19行程式碼是選取後端伺服器的關鍵,那麼這個條件是如何確保選取後端伺服器負載均衡呢?
假設有三臺後端伺服器A、B、C,它們的權值分別為5、3、1。那麼執行過程如下:
(1) 第一次請求由於peer[n].current_weight= peer[n].weight&&peer[i].current_weight= peer[i].weight,所以程式碼18行的條件始終不成立。13行的while迴圈到i=2時退出。接著執行到25程式碼行條件成立,n=i=2,所以第一次選中伺服器C,之後伺服器C的current_weight–,當前權值變為0。
(2) 第二次請求到來時,A、B、C的權值為5、3、0。程式碼執行到14行時,i=1,n=0,此時由於A和B的current_weight和weight相同,條件依然不成立,23行使n=i=1,然後i++變為2,但程式碼15行條件成立(C的current_weight為0),繼續迴圈到13行程式碼不成立。此時跳出13行的while迴圈,執行到18行返回n=1,即選擇伺服器B。
(3) 第三次請求到達時,A、B、C的權值為5、2、0。執行到程式碼14行時n=0,i=1,隨後18行條件成立(peer[n].current_weight=5,peer[i].current_weight=2,peer[n].weight=5,peer[i].weight=3),所以19行返回n=0,即選中伺服器A。
(4) ……
(5) 隨後請求處理類似,知道所有伺服器current_weight都等於0。此時第8行的for迴圈跳出,執行第30行條件不成立,執行33行,再次將current_weight重置為初始值。
這樣一個過程確保連結的處理按照伺服器配置的權重來均衡。
- 注意
可以看出nginx每次選出的伺服器並不一定是當前權重(處理能力)最大的,如上分析第一次請求選取的並不是伺服器A,而是C,但就總體效果而言如果請求數量足夠多最終可以實現讓客戶的請求在整體上根據伺服器的權值在各個伺服器上按照對應比例分佈。
- 應用
應用這個負載均衡邏輯就可以實現對客戶端的請求按照伺服器的處理能力(權重)進行負載均衡了。
打賞支援我寫出更多好文章,謝謝!
打賞作者
打賞支援我寫出更多好文章,謝謝!