熟練掌握Nginx負載均衡的使用對運維人員來說是極其重要的!下面針對Nignx負載均衡upstream容錯機制的使用做一梳理性說明:
一、nginx的upstream容錯
1)nginx 判斷節點失效狀態
Nginx默認判斷失敗節點狀態以connect refuse和time out狀態為准,不以HTTP錯誤狀態進行判斷失敗,因為HTTP只要能返回狀態說明該節點還可以正常連接,所以nginx判斷其還是存活狀態;除非添加了proxy_next_upstream指令設置對404、502、503、504、500和time out等錯誤進行轉到備機處理,在next_upstream過程中,會對fails進行累加,如果備用機處理還是錯誤則直接返回錯誤信息(但404不進行記錄到錯誤數,如果不配置錯誤狀態也不對其進行錯誤狀態記錄),綜述,nginx記錄錯誤數量只記錄timeout 、connect refuse、502、500、503、504這6種狀態,timeout和connect refuse是永遠被記錄錯誤狀態,而502、500、503、504只有在配置proxy_next_upstream后nginx才會記錄這4種HTTP錯誤到fails中,當fails大於等於max_fails時,則該節點失效;
2)nginx 處理節點失效和恢復的觸發條件
nginx可以通過設置max_fails(最大嘗試失敗次數)和fail_timeout(失效時間,在到達最大嘗試失敗次數后,在fail_timeout的時間范圍內節點被置為失效,除非所有節點都失效,否則該時間內,節點不進行恢復)對節點失敗的嘗試次數和失效時間進行設置,當超過最大嘗試次數或失效時間未超過配置失效時間,則nginx會對節點狀會置為失效狀態,nginx不對該后端進行連接,直到超過失效時間或者所有節點都失效后,該節點重新置為有效,重新探測;
3)所有節點失效后nginx將重新恢復所有節點進行探測
如果探測所有節點均失效,備機也為失效時,那么nginx會對所有節點恢復為有效,重新嘗試探測有效節點,如果探測到有效節點則返回正確節點內容,如果還是全部錯誤,那么繼續探測下去,當沒有正確信息時,節點失效時默認返回狀態為502,但是下次訪問節點時會繼續探測正確節點,直到找到正確的為止。
4)通過proxy_next_upstream實現容災和重復處理問題
ngx_http_proxy_module 模塊中包括proxy_next_upstream指令
語法: proxy_next_upstream error | timeout | invalid_header | http_500 | http_502 | http_503 | http_504 |http_404 | off ...;
默認值: proxy_next_upstream error timeout; 上下文: http, server, location
其中:
error 表示和后端服務器建立連接時,或者向后端服務器發送請求時,或者從后端服務器接收響應頭時,出現錯誤。
timeout 表示和后端服務器建立連接時,或者向后端服務器發送請求時,或者從后端服務器接收響應頭時,出現超時。
invalid_header 表示后端服務器返回空響應或者非法響應頭
http_500 表示后端服務器返回的響應狀態碼為500
http_502 表示后端服務器返回的響應狀態碼為502
http_503 表示后端服務器返回的響應狀態碼為503
http_504 表示后端服務器返回的響應狀態碼為504
http_404 表示后端服務器返回的響應狀態碼為404
off 表示停止將請求發送給下一台后端服務器
運用場景
1)proxy_next_upstream http_500 | http_502 | http_503 | http_504 |http_404; 當其中一台返回錯誤碼404,500...等錯誤時,可以分配到下一台服務器程序繼續處理,提高平台訪問成功率,多可運用於前台程序負載,設置 2、proxy_next_upstream off 因為proxy_next_upstream 默認值: proxy_next_upstream error timeout; 場景: 當訪問A時,A返回error timeout時,訪問會繼續分配到下一台服務器處理,就等於一個請求分發到多台服務器,就可能出現多次處理的情況, 如果涉及到充值,就有可能充值多次的情況,這種情況下就要把proxy_next_upstream關掉即可 proxy_next_upstream off 案例分析(nginx proxy_next_upstream導致的一個重復提交錯誤): 一個請求被重復提交,原因是nginx代理后面掛着2個服務器,請求超時的時候(其實已經處理了),結果nigix發現超時,有把請求轉給另外台服務器又做了次處理。 解決辦法: proxy_next_upstream:off
二、nginx負載均衡
Nginx的負載均衡方式這里介紹4種:rr(輪詢模式)、ip_hash、fair、url_hash;
Nginx自帶的2種負載均衡為rr和ip_hash,fair和url_hash為第三方的插件,nginx在不配置負載均衡的模式下,默認采用rr負載均衡模式。
1)RR負載均衡模式:
每個請求按時間順序逐一分配到不同的后端服務器,如果超過了最大失敗次數后(max_fails,默認1),在失效時間內(fail_timeout,默認10秒),該節點失效權重變為0,超過失效時間后,則恢復正常,或者全部節點都為down后,那么將所有節點都恢復為有效繼續探測,一般來說rr可以根據權重來進行均勻分配。
2)Ip_hash負載均衡模式:
每個請求按訪問ip的hash結果分配,這樣每個訪客固定訪問一個后端服務器,可以解決session的問題,但是ip_hash會造成負載不均,有的服務請求接受多,有的服務請求接受少,所以不建議采用ip_hash模式,session共享問題可用后端服務的session共享代替nginx的ip_hash。
3)Fair(第三方)負載均衡模式:
按后端服務器的響應時間來分配請求,響應時間短的優先分配。
4)url_hash(第三方)負載均衡模式:
和ip_hash算法類似,是對每個請求按url的hash結果分配,使每個URL定向到一個同 一個后端服務器,但是也會造成分配不均的問題,這種模式后端服務器為緩存時比較好。
三、Nginx負載均衡配置
Nginx的負載均衡采用的是upstream模塊,其中默認的采用的負載均衡模式是輪詢模式rr(round_robin),具體配置如下:
1)指令:
ip_hash
語法:ip_hash
默認值:none
使用字段:upstream
這個指令將基於客戶端連接的IP地址來分發請求。
哈希的關鍵字是客戶端的C類網絡地址,這個功能將保證這個客戶端請求總是被轉發到一台服務器上,但是如果這台服務器不可用,那么請求將轉發到另外的服務器上,這將保證某個客戶端有很大概率總是連接到一台服務器。
無法將權重(weight)與ip_hash聯合使用來分發連接。如果有某台服務器不可用,你必須標記其為"down",如下例:
upstream backend { ip_hash; server backend1.kevin.com; server backend2.kevin.com; server backend3.kevin.com down; server backend4.kevin.com; }
server
語法:server name [parameters]
默認值:none
使用字段:upstream
指定后端服務器的名稱和一些參數,可以使用域名,IP,端口,或者unix socket。如果指定為域名,則首先將其解析為IP。
[1] weight = NUMBER - 設置服務器權重,默認為1。
[2] max_fails = NUMBER - 在一定時間內(這個時間在fail_timeout參數中設置)檢查這個服務器是否可用時產生的最多失敗請求數,默認為1,將其設置為0可以關閉檢查,這些錯誤在proxy_next_upstream或fastcgi_next_upstream(404錯誤不會使max_fails增加)中定義。
[3] fail_timeout = TIME - 在這個時間內產生了max_fails所設置大小的失敗嘗試連接請求后這個服務器可能不可用,同樣它指定了服務器不可用的時間(在下一次嘗試連接請求發起之前),默認為10秒,fail_timeout與前端響應時間沒有直接關系,不過可以使用proxy_connect_timeout和proxy_read_timeout來控制。
[4] down - 標記服務器處於離線狀態,通常和ip_hash一起使用。
[5] backup - (0.6.7或更高)如果所有的非備份服務器都宕機或繁忙,則使用本服務器(無法和ip_hash指令搭配使用)。
實例配置
upstream backend { server backend1.kevin.com weight=5; server 127.0.0.1:8080 max_fails=3 fail_timeout=30s; server unix:/tmp/backend3; }
注意:如果你只使用一台上游服務器,nginx將設置一個內置變量為1,即max_fails和fail_timeout參數不會被處理。
結果:如果nginx不能連接到上游,請求將丟失。
解決:使用多台上游服務器。
upstream
語法:upstream name { … }
默認值:none
使用字段:http
這個字段設置一群服務器,可以將這個字段放在proxy_pass和fastcgi_pass指令中作為一個單獨的實體,它們可以可以是監聽不同端口的服務器,並且也可以是同時監聽TCP和Unix socket的服務器。
服務器可以指定不同的權重,默認為1。
示例配置
upstream backend { server kevin.com weight=5; server 127.0.0.1:8080 max_fails=3 fail_timeout=30s; server unix:/tmp/backend3; }
請求將按照輪詢的方式分發到后端服務器,但同時也會考慮權重。
在上面的例子中如果每次發生7個請求,5個請求將被發送到backend1.kevin.com,其他兩台將分別得到一個請求,如果有一台服務器不可用,那么請求將被轉發到下一台服務器,直到所有的服務器檢查都通過。如果所有的服務器都無法通過檢查,那么將返回給客戶端最后一台工作的服務器產生的結果。
2) 變量
版本0.5.18以后,可以通過log_module中的變量來記錄日志:
log_format timing '$remote_addr - $remote_user [$time_local] $request '
'upstream_response_time $upstream_response_time '
'msec $msec request_time $request_time';
log_format up_head '$remote_addr - $remote_user [$time_local] $request '
'upstream_http_content_type $upstream_http_content_type';
$upstream_addr
前端服務器處理請求的服務器地址
$upstream_cache_status
顯示緩存的狀態
nginx在web應用上的占用率越來越高,其帶的模塊也越來越來。nginx_cache算是一個,雖和專業的cache工具相比略遜一籌,但畢竟部署簡單,不用另裝軟件 和資源開銷,所以在web cache中也占了比重不小的一席。不過像squid和varnish等cache軟件都自帶的有cache查看工具,而且還可以方便的在http header上 顯示出是否命中。nginx主要還是做web使用。所以想要得出命中率的大小,還需要通過日志進行統計,不過想要增加header查看倒很簡單 1)在http header上增加命中顯示 nginx提供了$upstream_cache_status這個變量來顯示緩存的狀態,我們可以在配置中添加一個http頭來顯示這一狀態,達到類似squid的效果。 location / { proxy_redirect off; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_connect_timeout 180; proxy_send_timeout 180; proxy_read_timeout 180; proxy_buffer_size 128k; proxy_buffers 4 128k; proxy_busy_buffers_size 128k; proxy_temp_file_write_size 128k; proxy_cache cache; proxy_cache_valid 200 304 1h; proxy_cache_valid 404 1m; proxy_cache_key $uri$is_args$args; add_header Nginx-Cache "$upstream_cache_status"; proxy_pass http://backend; } 而通過curl或瀏覽器查看到的header如下: HTTP/1.1 200 OK Date: Mon, 22 Apr 2013 02:10:02 GMT Server: nginx Content-Type: image/jpeg Content-Length: 23560 Last-Modified: Thu, 18 Apr 2013 11:05:43 GMT Nginx-Cache: HIT Accept-Ranges: bytes Vary: User-Agent $upstream_cache_status包含以下幾種狀態: ·MISS 未命中,請求被傳送到后端 ·HIT 緩存命中 ·EXPIRED 緩存已經過期請求被傳送到后端 ·UPDATING 正在更新緩存,將使用舊的應答 ·STALE 后端將得到過期的應答 ======================================================================================================================= nginx比較強大,可以針對單個域名請求做出單個連接超時的配置. 可以根據業務的: proxy_connect_timeout :后端服務器連接的超時時間_發起握手等候響應超時時間 proxy_read_timeout:連接成功后,等候后端服務器響應時間_其實已經進入后端的排隊之中等候處理(也可以說是后端服務器處理請求的時間) proxy_send_timeout :后端服務器數據回傳時間_就是在規定時間之內后端服務器必須傳完所有的數據
$upstream_status
前端服務器的響應狀態。
$upstream_response_time
前端服務器的應答時間,精確到毫秒,不同的應答以逗號和冒號分開。
$upstream_http_$HEADER
隨意的HTTP協議頭,如:$upstream_http_host
$upstream_http_host
3) Proxy指令
proxy_next_upstream
語法:proxy_next_upstream
[error|timeout|invalid_header|http_500|http_502|http_503|http_504|http_404|off]
默認值:proxy_next_upstream error timeout
使用字段:http, server, location
確定在何種情況下請求將轉發到下一個服務器:
error 在連接到一個服務器,發送一個請求,或者讀取應答時發生錯誤。
timeout 在連接到服務器,轉發請求或者讀取應答時發生超時。
invalid_header 服務器返回空的或者錯誤的應答。
http_500 服務器返回500代碼。
http_502 服務器返回502代碼。
http_503 服務器返回503代碼。
http_504 服務器返回504代碼。
http_404 服務器返回404代碼。
off 禁止轉發請求到下一台服務器。
轉發請求只發生在沒有數據傳遞到客戶端的過程中。
其中記錄到nginx后端錯誤數量的有500、502、503、504、timeout,404不記錄錯誤。
proxy_connect_timeout
語法:proxy_connect_timeout timeout_in_seconds
默認值:proxy_connect_timeout 60s
使用字段:http, server, location
指定一個連接到代理服務器的超時時間,單位為秒,需要注意的是這個時間最好不要超過75秒。
這個時間並不是指服務器傳回頁面的時間(這個時間由proxy_read_timeout聲明)。
如果你的前端代理服務器是正常運行的,但是遇到一些狀況(如沒有足夠的線程去處理請求,請求將被放在一個連接池中延遲處理),那么這個聲明無助於服務器去建立連接。
可以通過指定時間單位以免引起混亂,支持的時間單位有”s”(秒), “ms”(毫秒), “y”(年), “M”(月), “w”(周), “d”(日), “h”(小時),和 “m”(分鍾)。
這個值不能大於597小時。
proxy_read_timeout
語法:proxy_read_timeout time
默認值:proxy_read_timeout 60s
使用字段:http, server, location
決定讀取后端服務器應答的超時時間,單位為秒,它決定nginx將等待多久時間來取得一個請求的應答。超時時間是指完成了兩次握手后並且狀態為established的超時時間。
相對於proxy_connect_timeout,這個時間可以撲捉到一台將你的連接放入連接池延遲處理並且沒有數據傳送的服務器,注意不要將此值設置太低,某些情況下代理服務器將花很長的時間來獲得頁面應答(例如如當接收一個需要很多計算的報表時),當然你可以在不同的location里面設置不同的值。
可以通過指定時間單位以免引起混亂,支持的時間單位有”s”(秒), “ms”(毫秒), “y”(年), “M”(月), “w”(周), “d”(日), “h”(小時),和 “m”(分鍾)。
這個值不能大於597小時。
proxy_send_timeout
語法:proxy_send_timeout seconds
默認值:proxy_send_timeout 60s
使用字段:http, server, location
設置代理服務器轉發請求的超時時間,單位為秒,同樣指完成兩次握手后的時間,如果超過這個時間代理服務器沒有數據轉發到被代理服務器,nginx將關閉連接。
可以通過指定時間單位以免引起混亂,支持的時間單位有”s”(秒), “ms”(毫秒), “y”(年), “M”(月), “w”(周), “d”(日), “h”(小時),和 “m”(分鍾)。
這個值不能大於597小時。
四、Nginx upstream負載均衡獲取后端服務器的流程
GET_RR_PEER: 通過RR算法獲取后端流程
K:是判斷peer是否宕機和判斷失效狀態算法
FAIL:嘗試次數用盡有,跳轉到失敗流程,如果有備機,備機再嘗試監聽,如果監聽失敗則返回NGX_BUSY,成功則返回當前狀態。
五、驗證環境部署
Web服務器: nginx
Web應用服務器:tomcat(2台)
Nginx反向代理tomcat,即通過upstream將請求負載到后端兩台tomcat的對應服務端口上。部署過程此處省略......
六、驗證結果說明
1)設置tomcat1超時時間,造成超時狀態(總有一台server為有效狀態)
Tomcat1的connectionTimeout 設置為-1,永遠超時,nginx設置tomcat1和tomcat2權重為10,tomcat1的max_fails為10,fail_timeout=120;在連接tomcat1的10次后,返回給nginx為10次超時,ngxin判斷tomcat1為失效,然后將tomcat1超時時間恢復為1000重新啟動tomcat1,在這段時間內nginx判斷tomcat1還是失效狀態,所以在2分鍾后,nginx繼續監聽到tomcat1正常后,那么nginx會將tomcat1判斷為有效,將連接繼續均勻分配到2個tomcat上。
2)設置tomcat1連接數量,造成超時狀態(總有一台server為有效狀態)
Tomcat1的線程數量設置為1,nginx設置tomcat1和tomcat2權重為10,tomcat1的max_fails為10,fail_timeout=120;在連接tomcat1超過線程接受數量后,tomcat1會返回超時狀態,在返回給nginx10次超時狀態后,ngxin判斷tomcat1為失效,然后將tomcat線程數量恢復為700,重新啟動tomcat1,在這段時間內nginx判斷tomcat1還是失效狀態,超過2分鍾失效后,nginx繼續監聽到tomcat1正常后,那么nginx會將tomcat1判斷為有效,將連接繼續均勻分配到2個tomcat上。
3)設置tomcat1關閉,造成拒絕狀態(總有一台server為有效狀態)
Tomcat1為關閉,nginx設置tomcat1和tomcat2權重為10,tomcat1的max_fails為10,fail_timeout=120;在連接tomcat1的10次后,nginx收到tomcat1返回connect refuse狀態,ngxin判斷tomcat1為失效,然后重新啟動tomcat1,在這段時間內nginx判斷tomcat1還是失效狀態,超過2分鍾失效后,nginx繼續監聽到tomcat1正常后,那么nginx會將tomcat1判斷為有效,將連接繼續均勻分配到2個tomcat上。
4)設置tomcat1在nginx1標記失效,tomcat1恢復正常,在nginx失效范圍內,將全部服務變為失效,然后重啟
Tomcat1為關閉,nginx設置tomcat1和tomcat2權重為10,tomcat1的max_fails為10,fail_timeout=120;在連接tomcat1的10次后,nginx收到tomcat1返回connect refuse狀態,ngxin判斷tomcat1為失效,然后重新啟動tomcat1,在這段時間內nginx判斷tomcat1還是失效狀態,然后將tomcat2關閉,然后重啟tomcat2,由於所有服務均失效,所以nginx 將所有服務重新置為有效進行監聽,然后將2連接均勻分布到了tomcat1和tomcat2上。
5)http錯誤狀態,nginx是否記錄失效
nginx設置tomcat1和tomcat2權重為10,tomcat1的max_fails為10,fail_timeout=120;配置proxy_next_upstream 500、404、502、503、504、timeout后,當HTTP狀態為500、502、503、504(timeout和refuse默認是記錄失效的)時,nginx會判斷該次請求為失敗記錄失敗狀態,其他所有HTTP均不記錄失敗。