Nginx負載均衡與反向代理

NO IMAGE
1 Star2 Stars3 Stars4 Stars5 Stars 給文章打分!
Loading...

我們使用Nginx時,大多數場景下使用的都是七層的HTTP負載均衡(ngx_http_upstream_module)。在1.9.0版本以後,Nginx也開始支援TCP(ngx_stream_upstream_module)四層負載均衡。

四層/七層負載均衡的區別

四層負載均衡,就是基於IP 埠的負載均衡(TCP/UDP)。七層負載均衡,就是基於URL等應用層協議(HTTP)的負載均衡。熟悉網路分層協議的同學就很容易推理到,還會有基於MAC地址的二層負載均衡和基於IP地址的三層負載均衡。二層負載均衡會通過一個虛擬的MAC地址接收請求,然後再分配到真實的MAC地址上去。三層負載均衡會通過一個VIP(虛擬IP)接收請求,然後再分配到真實的IP上去。四層負載均衡會通過IP 埠接收請求,然後再分配到真實的伺服器。七層負載均衡通過虛擬的URL或者HOST接收請求,然後再分配到真實的伺服器上去。

所謂的四層到七層的負載均衡,就是依據四層及以下、七層及以下的資訊來決定如何轉發。比如四層的負載均衡,就是利用三層的VIP,然後加上四層的埠號,來決定流量如何來進行負載均衡。

對於負載均衡我們需要關注以下幾點:

  • 上游伺服器配置:使用upstream server配置上游伺服器。
  • 負載均衡演算法:配置多個上游伺服器的負載均衡機制。
  • 失敗重試機制:配置當超時或者上游伺服器不存活時,是否需要重試其他伺服器。
  • 伺服器心跳檢查:上游伺服器的健康檢查/心跳檢查。

upstream配置

upstream就是真實處理業務的伺服器,upstream在http指令下:

upstream backend {
server 192.168.0.1:8080 weight=1;
server 192.168.0.2:8080 weight=2;
}

upstream server的配置如下:

  • IP地址和埠:配置上游伺服器的IP地址和埠;
  • 權重:weight用來配置權重,預設1。權重越大,分發的請求越多。如上所示:3個請求:1個到192.168.0.1,2個到192.168.0.2。

配置完上游伺服器(upstream)之後,就要配置proxy_pass來處理使用者請求。

location / {
proxy_pass http://backend;
}

負載均衡演算法

負載均衡策略用來解決請求到來時,如何選擇upstream server進行處理,預設採用的是round-robin(輪詢)。

  • round-robin:輪詢,預設負載均衡演算法,通過配合weight配置實現基於權重的輪詢。
  • ip_hash:根據客戶IP進行負載均衡,相同的IP會被負載均衡到同一個upstream,配置如下:
upstream backend {
ip_hash;
server 192.168.0.1:8080 weight=1;
server 192.168.0.2:8080 weight=2;
}
  • hash_key [consistent]:對於一個key進行hash或者使用一致性雜湊演算法進行負載均衡。Hash演算法存在的問題是:當增加/刪除一臺伺服器時,將導致很多key被重新負載均衡到不同的伺服器。從而有可能導致使用者訪問出問題。因此可以考慮一致性雜湊,伺服器橫向擴充套件時,只有少部分機器會被重新分配。
  • 雜湊演算法:此處是根據uri進行負載均衡,可以使用Nginx變數,從而實現複雜的演算法。
upstream backend {
hash $uri;
server 192.168.0.1:8080 weight=1;
server 192.168.0.2:8080 weight=2;
}
  • 一致性雜湊演算法:consistent_key動態指定。
upstream nginx_local_server {
hash $consistent_key consistent;
server 192.168.0.1:8080 weight=1;
server 192.168.0.2:8080 weight=2;
}

下面看一下根據引數cat(類目),做的稍微複雜一點的負載均衡:

location / {
set $consistent_key $arg_cat;
if ($consistent_key = " ") {
set $consistent_key $request_uri;
}
}

而在實際使用過程中,更多的是使用Lua指令碼進行處理。

set_by_lua_file $consistent_key "lua_balancing.lua";

lua_balancing.lua

local consistent_key = args.cat
if not consistent_key or consistent_key == '' then
consistent_key = ngx_var.request_uri
end
local value = balancing_cache:get(consistent_key)
if not value then
success, err = balancing_cache:set(consistent_key, 1, 60)
else
newval, err = balancing_cache:incr(consistent_key, 1)
end

如上所示,如果其中一個分類在60S內的請求過多,則可以通過以下改進來平滑請求。

local consistent_key = args.cat
if not consistent_key or consistent_key == '' then
consistent_key = ngx_var.request_uri
end
-- 大於5000時,生成新key
local value = balancing_cache:get(consistent_key)
if value > 5000 then
consistent_key = consistent_key .. '_' .. value
end
if not value then
success, err = balancing_cache:set(consistent_key, 1, 60)
else
newval, err = balancing_cache:incr(consistent_key, 1)
end
  • least_conn:將請求負載均衡到最小活躍連線的上游伺服器。如果配置的伺服器較少,則轉為基於權重輪詢的演算法。

除了上面的負載均衡策略,商業版的Nginx還提供了least_time,就是基於最小平均響應時間進行負載均衡。

失敗重試

失敗重試主要包含兩部分配置:upstream server和proxy_pass。

upstream backend {
server 192.168.0.1:8080 max_fails=2 fail_timeout=10s weight=1;
server 192.168.0.2:8080 max_fails=2 fail_timeout=10s weight=1;
}

通過配置上游伺服器的max_fails和fail_timeout,來指定每個上游伺服器,當fail_timeout時間內失敗了max_fails次請求,則認為該上游伺服器不可用。然後剔除該伺服器,fail_timeout時間後會再次將該伺服器加入到存活列表中進行重試。

location /test {
proxy_connect_timeout 5s;
proxy_read_timeout 5s;
proxy_send_timeout 5s;
proxy_next_upstream error timeout;
proxy_next_upstream_timeout 10s;
proxy_next_upstream_tries 2;
proxy_pass http://backend;
add_header upstream_addr $upstream_addr;
}

上述引數詳情請參見:《分散式系統超時重試

健康檢查

Nginx對上游伺服器的健康檢查採用的是惰性策略。(Nginx商業版提供了health_check的主動檢查)社群版的Nginx可以整合nginx_upstream_check_module進行主動健康檢查。配置如下:

TCP心跳檢查

upstream backend {
server 192.168.0.1:8080 weight=1;
server 192.168.0.2:8080 weight=2;
check interval=3000 rise=1 fall=3 timeout=2000 type=tcp;
}
  • interval:檢測間隔時間,此處配置了每隔3s檢測一次。
  • fall:檢測失敗多少次後,上游伺服器被標識為不存活。
  • rise:檢測成功多少次後,上游伺服器被標識為存活,並可以處理請求。
  • timeout:檢測請求超時時間配置。

HTTP心跳檢查

upstream backend {
server 192.168.0.1:8080 weight=1;
server 192.168.0.2:8080 weight=2;
check interval=3000 rise=1 fall=3 timeout=2000 type=http;
check_http_send "HEAD /status HTTP/1.0\r\n\r\n";
check_http_expect_alive http_2xx http_3xx;
}

HTTP心跳配置比TCP額外多了2個配置:

  • check_http_send:健康檢查時所傳送的請求內容。
  • check_http_expect_alive:當上遊伺服器返回匹配的狀態碼,就認為上游伺服器存活。

這裡面需要注意的是,健康檢查的時間間隔不宜過短。否則有可能會造成擁堵,更甚至造成上游伺服器掛掉。

長連線配置

可以使用keepalive指令配置Nginx與上游伺服器可快取的空閒連線的最大數量。當超出數量時,最近最少使用的連線將被關閉。keepalive指令不限制Worker程序與上游伺服器的總連線數。

upstream backend {
server 192.168.0.1:8080 weight=1;
server 192.168.0.2:8080 weight=2 backup;
keepalive 100;
}

要想與上游伺服器建立長連線,還需要如下配置:

location / {
# 支援 keep-alive
proxy_http_version 1.1;
proxy_set_header Connection "";
proxy_pass http://backend;
}

總長連線數=空閒連線池 釋放連線池。首先,長連線配置不會限制Worker程序可以開啟的總連線數(超出的作為短連線)。連線池需要根據不同的場景進行設定。

空閒連線池太小,連線不夠用,就需要不斷的重新建立連線;如果太大,就會造成還沒用就超時了。

其他配置

域名上游伺服器

upstream backend {
server c0.3.cn;
server c1.3.cn;
}

上面的配置在載入時,host就會被解析成IP。但是當host的IP變更時,IP不會改變。但是商業版的Nginx是支援動態變更IP的。另外proxy_pass http://c1.3.cn是可以支援動態解析的,但是這樣反向代理就只能配置一臺了,比較尷尬。還有一種解決方案就是lua指令碼動態解析。這裡不再贅述了。

備份上游伺服器

upstream backend {
server 192.168.0.1:8080 weight=1;
server 192.168.0.2:8080 weight=2 backup;
}

上面192.168.0.2被配置為備份伺服器,當所有上游主機都不存活時,請求就會被轉發給備份伺服器。

不可用伺服器

upstream backend {
server 192.168.0.1:8080 weight=1;
server 192.168.0.2:8080 weight=2 down;
}

當上遊伺服器出現故障時,可以通過該配置臨時摘除機器。

配置示例

除了反向代理之外,還可以使用快取來減少上游伺服器的壓力。

全域性配置(proxy cache)

proxy_buffering on;
proxy_buffer_size 4k;
proxy_buffers 512 4k;
proxy_busy_buffers_size 64k;
proxy_temp_file_write_size 256k;
proxy_cache_lock on;
proxy_cache_lock_timeout 200ms;
proxy_temp_path /tmp/proxy_temp;
proxy_cache_path /tmp/proxy_cache levels=1:2 keys_zone=cache:512m inactive=5m max_size=8g;
proxy_connect_timeout 3s;
proxy_read_timeout 5s;
proxy_send_timeout 5s;

開啟proxy buffer後,快取內容將存放在檔案系統中,從而提高系統效能。

location配置

location ~ ^/backend/(.*)$ {
# 設定一致性雜湊負載均衡key
set_by_lua_file $consistent_key "lua/balancing.lua";
# 失敗重試配置
proxy_next_upstream error timeout http_500 http_502 http_504;
proxy_next_upstream_timeout 2s;
proxy_next_upstream_tries 2;
# 請求上游伺服器使用GET方法(無論客戶端請求方法)
proxy_method GET;
# 不給上游伺服器傳遞請求體
proxy_pass_request_body off;
# 不給上游伺服器傳遞請求頭
proxy_pass_request_headers off;
# 設定上游伺服器哪些響應頭不傳送給客戶端
proxy_hide_header Vary;
# 支援keep-alive
proxy_http_version 1.1;
proxy_set_header Connection "";
# 給上游伺服器傳遞Referer、Cookie和Host(按需傳遞)
proxy_set_header Referer $http_referer;
proxy_set_header Cookie $http_cookie;
proxy_set_header Host www.moguhu.com;
proxy_pass http://backend /$1$is_args$args;
}

通常情況下,為了減少網路開銷,一般會使用gzip來減少網路資料包的大小。

gzip on;
gzip_min_length 1k;
gzip_buffers 16 16k;
gzip_http_version 1.1;
gzip_proxied any;
gzip_comp_level 2;
gzip_types text/plain application/x-javascript text/css application/xml;
gzip_vary on;

參考:《億級流量網站架構核心技術》
連結:http://moguhu.com/article/detail?articleId=90

相關文章

程式語言 最新文章