分析NGINX 健康檢查和負載均衡機制


nginx 是優秀的反向代理服務器,這里主要講它的健康檢查和負載均衡機制,以及這種機制帶來的問題。所謂健康檢查,就是當后端出現問題(具體什么叫出現問題,依賴於具體實現,各個實現定義不一樣),不再往這個后端分發請求,並且做后續的檢查,直到這個后端恢復正常。所謂負載均衡,就是選擇后端的方式,如何(根據后端的能力)將請求均衡的分發到后端。此外,當請求某個后端失敗時,要將該請求分發到其它后端(redispatch)。這里以ngx_http_upstream_round_robin(簡稱RR)做為負載均衡模塊,以ngx_http_proxy_module(檢查proxy)作為后端代理模塊。

 

nginx 的健康檢查和負載均衡是密切相關的,它沒有獨立的健康檢查模塊,而是使用業務請求作為健康檢查,這省去了獨立健康檢查線程,這是好處。壞處是,當業務復雜時,可能出現誤判,例如后端響應超時,這是可能是后端宕機,也可能是某個業務請求自身出現問題,跟后端無關。如果后端宕機,nginx還要在將它標記為不可用之后,仍不時的將業務請求分發給它,以檢查后端是否恢復。

 

nginx 完成客戶端請求Header部分的解析,upstream 調用RR模塊的peer.get 選擇具體的后端。當請求結束,upstream 調用RR模塊的peer.free,向RR反饋后端的健康情況。當upstream和后端通信時,出現錯誤會調用 ngx_http_upstream_next,

void ngx_http_upstream_next(ngx_http_request_t *r, ngx_http_upstream_t *u, ngx_uint_t ft_type); 第三個參數指明錯誤類型,共有如下錯誤類型

 

#define NGX_HTTP_UPSTREAM_FT_ERROR           0x00000002

#define NGX_HTTP_UPSTREAM_FT_TIMEOUT         0x00000004

#define NGX_HTTP_UPSTREAM_FT_INVALID_HEADER  0x00000008

#define NGX_HTTP_UPSTREAM_FT_HTTP_500        0x00000010

#define NGX_HTTP_UPSTREAM_FT_HTTP_502        0x00000020

#define NGX_HTTP_UPSTREAM_FT_HTTP_503        0x00000040

#define NGX_HTTP_UPSTREAM_FT_HTTP_504        0x00000080

#define NGX_HTTP_UPSTREAM_FT_HTTP_404        0x00000100

#define NGX_HTTP_UPSTREAM_FT_UPDATING        0x00000200

#define NGX_HTTP_UPSTREAM_FT_BUSY_LOCK       0x00000400

#define NGX_HTTP_UPSTREAM_FT_MAX_WAITING     0x00000800

#define NGX_HTTP_UPSTREAM_FT_NOLIVE          0x40000000

 

ngx_http_upstream_next,只要錯誤類型不是 NGX_HTTP_UPSTREAM_FT_HTTP_404,都認為后端有問題(NGX_PEER_FAILED)

if (ft_type == NGX_HTTP_UPSTREAM_FT_HTTP_404) {                                                              

   state = NGX_PEER_NEXT;                                                                                   

} else {                                                                                                     

   state = NGX_PEER_FAILED;                                                                                 

}

ngx_http_upstream_next 調用RR的peer.free,RR根據state判斷剛才接受請求的后端是否健康。

if (ft_type != NGX_HTTP_UPSTREAM_FT_NOLIVE) {

   u->peer.free(&u->peer, u->peer.data, state);

}

 

ngx_http_upstream_next 如果超過最大重試次數(默認為后端的個數,每試過一個,就減1),或者proxy設置不允許redispatch,則向客戶端返回響應status。

if (u->peer.tries == 0 || !(u->conf->next_upstream & ft_type)) {

   ngx_http_upstream_finalize_request(r, u, status);

}

proxy 模塊的 proxy_next_upstream 配置,在何種情況下將請求redispatch到下一個后端。

 

剛剛談到,只要錯誤類型不是 NGX_HTTP_UPSTREAM_FT_HTTP_404,都認為后端有問題。這里的錯誤類型包括,連接后端失敗,連接,讀寫后端超時,后端返回了500,502,504等。這個策略是有待商榷的,尤其是讀寫后端超時也判斷為后端不可用。因為某個業務請求,可能因為自身的原因而導致讀寫超時。注意,在proxy_next_upstream 中指定timeout,http_504 是不同的,前者表示upstream連接,讀寫后端超時,后者表示后端返回的http code 是504。

 

實際上健康檢查不是必須的,因為redispatch的存在保證了,就算有后端宕機,客戶端仍將收到正確的響應。那么我們考慮關掉健康檢查。通過upstream 的server配置的max_fails 參數

 

RR 的peer.get,如果max_fails 為0,則該后端總是可用的(就算它真有問題)。

if (peer->max_fails == 0

   || peer->fails < peer->max_fails)

{   

   break;

}

 

因為redispatch的次數,取決於后端的個數,所以后端的個數稍微多一點是有好處的。

 

下面是一些佐證分析的測試。

 

upstream test {

   server 127.0.0.1:8060 max_fails=0;

   server 127.0.0.1:8070 max_fails=0;

   server 127.0.0.1:8080 max_fails=0;

   server 127.0.0.1:8090 max_fails=0;

}

只有8060,8070是存活的,8080,8090處於不可用狀態,這里max_fails=0,關閉了健康檢查。

 

proxy_read_timeout 2;

讀超時設為2S。

 

proxy_next_upstream error timeout;

默認當 error 和 timeout發生時,redispatch。

 

測試請求的sleep參數指定后端的sleep時間,code參數指定后端返回的http code。根據time和sleep時間的對比,判斷重試了幾個后端。

 

time curl "http://127.0.0.1:8099/index.php?sleep=3" -vv

real    0m4.014s

sleep=3,讀超時,重試了2個后端。

 

修改配置 proxy_next_upstream error;

 

time curl "http://127.0.0.1:8099/index.php?sleep=3" -vv

real    0m2.018s

讀超時,不再redispatch,重試了1個后端。

 

修改配置 proxy_next_upstream error http_504;

 

time curl "http://127.0.0.1:8099/index.php?sleep=1" -vv

real    0m1.022s

這個是正常請求。

 

time curl "http://127.0.0.1:8099/index.php?sleep=1&code=504" -vv

real    0m2.023s

讓后端返回504,此時nginx會做redispatch,重試了2個后端

但是nginx返回給客戶端的是502,不是504,因為所有的后端都返回504,nginx認為后端不可用,返回502.

 

測試健康檢查,關掉redispatch。proxy_next_upstream off;

 

curl "http://127.0.0.1:8099/index.php?sleep=3" -vv

返回了兩次502,兩次504。存活的后端返回504,有問題的返回502。

 

修改 max_fails server 127.0.0.1:8060 max_fails=1; 對8060開啟健康檢查。

 

curl "http://127.0.0.1:8099/index.php?sleep=3" -vv

第一輪4次請求,返回兩次502,兩次504

8080和8090有問題,返回502,8060和8070響應超時,返回504,因為8060開啟了健康檢查,並且返回了504,所以被標記為不可用。

第二輪4次請求,返回三次502,一次504。8070沒有開啟健康檢查,所以仍然返回504。

 

根據測試分析,業務請求(sleep 3s,或者 輸出 http 504)可以讓nginx誤以為后端宕了,而這時后端活得好好的。在私有雲平台,這個通常不是問題,把超時設大點,不返回5XX錯誤,可以避免這個問題。但是在公有雲平台,這是致命的,因為業務可以編程輸出5XX錯誤。有兩種方法應對,一種是關閉健康檢查,一種是修改nginx的代碼,僅對 NGX_HTTP_UPSTREAM_FT_ERROR 判定為后端有問題。


免責聲明!

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



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