Nginx負載平衡與反向 Proxy—《億級流量網站架構核心技術》

來源:互聯網
上載者:User

標籤:dns伺服器   nec   sentinel   hash   shell   提升   移除   安裝   fail   

當我們的應用單一實例不能支撐使用者請求時,此時就需要擴容,從一台伺服器擴容到兩台、幾十台、幾百台。
然而,使用者訪問時是通過如http://www.XX.com的方式訪問,在請求時,瀏覽器首先會查詢DNS伺服器擷取對應的IP,
然後通過此IP訪問對應的服務。
因此,一種方式是www.XX.com網域名稱映射多個IP,但是,存在一個最簡單的問題,假設某台伺服器重啟或者出現故障,
DNS會有一定的緩衝時間,故障後切換時間長,而且沒有對後端服務進行心跳檢查和失敗重試的機制。
因此,外網DNS應該用來實現用GSLB(全域負載平衡)進行流量調度,
如將使用者指派到離他最近的伺服器上以提升體驗。
而且當某一地區的機房出現問題時(如被挖斷了光纜),可以通過DNS指向其他地區的IP來使服務可用。

可以在站長之家使用“DNS查詢”,查詢c.3.cn可以看到類似如下的結果。
即不同的電訊廠商返回的公網IP是不一樣的。

對於內網DNS,可以實現簡單的輪詢負載平衡。
但是,還是那句話,會有一定的緩衝時間並且沒有失敗重試機制。
因此,我們可以考慮選擇如HaProxy和Nginx。
而對於一般應用來說,有Nginx就可以了。但Nginx一般用於七層負載平衡,其輸送量是有一定限制的。
為了提升整體輸送量,會在DNS和Nginx之間引入接入層,
如使用LVS(軟體負載平衡器)、F5(硬負載平衡器)可以做四層負載平衡,
即首先DNS解析到LVS/F5,然後LVS/F5轉寄給Nginx,再由Nginx轉寄給後端Real Server。

對於一般業務開發人員來說,我們只需要關心到Nginx層面就夠了,LVS/F5一般由系統/營運工程師來維護。
Nginx目前提供了HTTP(ngx_http_upstream_module)七層負載平衡,
而1.9.0版本也開始支援TCP(ngx_stream_upstream_module)四層負載平衡。


此處再澄清幾個概念。
二層負載平衡是通過改寫報文的目標MAC地址為上遊伺服器MAC地址,源IP地址和目標IP地址是沒有變的,
負載平衡伺服器和真實伺服器共用同一個VIP,如LVSDR工作模式。
四層負載平衡是根據連接埠將報文轉寄到上遊伺服器(不同的IP地址+連接埠),如LVSNAT模式、HaProxy,
七層負載平衡是根據連接埠號碼和應用程式層協議如HTTP協議的主機名稱、URL,轉寄報文到上遊伺服器(不同的IP地址+連接埠),如HaProxy、Nginx。


這裡再介紹一下LVS DR工作模式,其工作在資料連結層,LVS和上遊伺服器共用同一個VIP,通過改寫報文的目標MAC地址為上遊伺服器MAC地址實現負載平衡,上遊伺服器直接響應報文到用戶端,不經過LVS,從而提升效能。但因為LVS和上遊伺服器必須在同一個子網,為瞭解決跨子網問題而又不影響負載效能,可以選擇在LVS後邊掛HaProxy,通過四到七層負載平衡器HaProxy叢集來解決跨網和效能問題。
這兩個“半成品”的東西相互取長補短,組合起來就變成了一個“完整”的負載平衡器。現在Nginx的stream也支援TCP,所以Nginx也算是一個四到七層的負載平衡器,一般情境下可以用Nginx取代HaProxy。


講解幾個術語。接入層、反向 Proxy伺服器、負載平衡伺服器,
在本文中如無特殊說明則指的是Nginx。upstream server即上遊伺服器,
指Nginx負載平衡到的處理業務的伺服器,也可以稱之為real server,即真實處理業務的伺服器。


對於負載平衡我們要關心的幾個方面如下。

上遊伺服器配置:使用upstream server配置上遊伺服器。
負載平衡演算法:配置多個上遊伺服器時的負載平衡機制。
失敗重試機制:配置當逾時或上遊伺服器不存活時,是否需要重試其他上遊伺服器。
伺服器心跳檢查:上遊伺服器的健全狀態檢查/心跳檢查。


Nginx提供的負載平衡可以實現上遊伺服器的負載平衡、容錯移轉、失敗重試、容錯、健全狀態檢查等,
當某些上遊伺服器出現問題時可以將請求轉到其他上遊伺服器以保障高可用,並可以通過OpenResty實現更智能的負載平衡,
如將熱點與非熱點流量分離、正常流量與爬蟲流量分離等。
Nginx負載平衡器本身也是一台反向 Proxy伺服器,將使用者請求通過Nginx代理到內網中的某台上遊伺服器處理,
反向 Proxy伺服器可以對響應結果進行緩衝、壓縮等處理以提升效能。

2.1  upstream配置

第一步我們需要給Nginx配置上遊伺服器,即負載平衡到的真實處理業務的伺服器,通過在http指令下配置upstream即可。

upstream backend {

    server 192.168.61.1:9080  weight=1;

    server 192.168.61.1:9090  weight=2;

}

upstream server主要配置。

IP地址和連接埠:配置上遊伺服器的IP地址和連接埠。

權重:weight用來配置權重,預設都是1,權重越高分配給這台伺服器的請求就越多
(如上配置為每三次請求中一個請求轉寄給9080,其餘兩個請求轉寄給9090),
需要根據伺服器的實際處理能力設定權重(比如,物理伺服器和虛擬機器就需要不同的權重)。


然後,我們可以配置如下proxy_pass來處理使用者請求。

location / {

   proxy_pass http://backend;

}

當訪問Nginx時,會將請求反向 Proxy到backend配置的Upstream Server。接下來我們看一下負載平衡演算法。

 

2.2  負載平衡演算法
負載平衡用來解決使用者請求到來時如何選擇Upstream Server進行處理,預設採用的是round-robin(輪詢),同時支援其他幾種演算法。
round-robin:輪詢,預設負載平衡演算法,即以輪詢的方式將請求轉寄到上遊伺服器,通過配合weight配置可以實現基於權重的輪詢。
ip_hash:根據客戶IP進行負載平衡,即相同的IP將負載平衡到同一個Upstream Server。

upstream backend {

    ip_hash;

    server 192.168.61.1:9080  weight=1;

    server 192.168.61.1:9090  weight=2;

}

hash key [consistent]:對某一個key進行雜湊或者使用一致性雜湊演算法進行負載平衡。使用Hash演算法存在的問題是,當添加/刪除一台伺服器時,將導致很多key被重新負載平衡到不同的伺服器(從而導致後端可能出現問題);因此,建議考慮使用一致性雜湊演算法,這樣當添加/刪除一台伺服器時,只有少數key將被重新負載平衡到不同的伺服器。

雜湊演算法:此處是根據請求uri進行負載平衡,可以使用Nginx變數,因此,可以實現複雜的演算法。
upstream backend {

    hash $uri;

    server 192.168.61.1:9080  weight=1;

    server 192.168.61.1:9090  weight=2;

}

一致性雜湊演算法:consistent_key動態指定。
upstream nginx_local_server {

    hash $consistent_key consistent;

    server 192.168.61.1:9080  weight=1;

    server 192.168.61.1:9090  weight=2;

}

如下location指定了一致性雜湊key,此處會優先考慮求參數cat(類目),如果沒有,則再根據請求uri進行負載平衡。
location / {

    set $consistent_key $arg_cat;

    if($consistent_key = "") {

        set $consistent_key $request_uri;

    }

}

而實際我們是通過lua設定一致性雜湊key。

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

如果某一個分類請求量太大,上遊伺服器可能處理不了這麼多的請求,此時可以在一致性雜湊key後加上遞增的計數以實作類別似輪詢的演算法。
if newval > 5000 then

   consistent_key = consistent_key .. ‘_‘ .. newval

end

least_conn:將請求負載平衡到最少活躍串連的上遊伺服器。如果配置的伺服器較少,則將轉而使用基於權重的輪詢演算法。

Nginx商業版還提供了least_time,即基於最小平均回應時間進行負載平衡。

 

2.3  失敗重試

主要有兩部分配置:upstream server和proxy_pass。

upstream backend {

    server 192.168.61.1:9080 max_fails=2 fail_timeout=10s weight=1;

    server 192.168.61.1:9090 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_upstreamerror timeout;

    proxy_next_upstream_timeout 10s;

   proxy_next_upstream_tries 2;

 

   proxy_pass http://backend;

   add_header upstream_addr $upstream_addr;

}

}

然後進行proxy_next_upstream相關配置,當遇到配置的錯誤時,會重試下一台上遊伺服器。

詳細配置請參考“代理層逾時與重試機制”中的Nginx部分。

 

2.4  健全狀態檢查

Nginx對上遊伺服器的健全狀態檢查預設採用的是惰性策略,Nginx商業版提供了health_check進行主動健全狀態檢查。當然也可以整合nginx_upstream_check_module(https://github.com/yaoweibin/nginx_upstream_check_module)模組來進行主動健全狀態檢查。
nginx_upstream_check_module支援TCP心跳和HTTP心跳來實現健全狀態檢查。


2.4.1  TCP心跳檢查

upstream backend {

    server 192.168.61.1:9080  weight=1;

    server 192.168.61.1:9090  weight=2;

    check interval=3000 rise=1 fall=3 timeout=2000 type=tcp;

}

此處配置使用TCP進行心跳檢測。

interval:活動訊號間隔時間時間,此處配置了每隔3s檢測一次。
fall:檢測失敗多少次後,上遊伺服器被標識為不存活。
rise:檢測成功多少次後,上遊伺服器被標識為存活,並可以處理請求。
timeout:檢測請求逾時時間配置。


2.4.2  HTTP心跳檢查

upstream backend {
    server 192.168.61.1:9080  weight=1;
    server 192.168.61.1:9090  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心跳檢查有如下兩個需要額外配置。
check_http_send:即檢查時發的HTTP請求內容。
check_http_expect_alive:當上遊伺服器返回匹配的響應狀態代碼時,則認為上遊伺服器存活。
此處需要注意,檢查間隔時間不能太短,否則可能因為心跳檢查包太多造成上遊伺服器掛掉,同時要設定合理的逾時時間。
本文使用的是openresty/1.11.2.1(對應nginx-1.11.2),安裝Nginx之前需要先打nginx_upstream_check_module補丁(check_1.9.2+.patch),到Nginx目錄下執行如下shell:
patch -p0 < /usr/servers/nginx_upstream_check_module-master/check_1.9.2+.patch。
如果不安裝補丁,那麼nginx_upstream_check_module模組是不工作的,建議使用wireshark抓包查看其是否工作。
2.5  其他配置
2.5.1  網域名稱上遊伺服器

upstream backend {

    server c0.3.cn;

    server c1.3.cn;

}

Nginx社區版,是在Nginx解析設定檔的階段將網域名稱解析成IP地址並記錄到upstream上,當這兩個網域名稱對應的IP地址發生變化時,該upstream不會更新。Nginx商業版才支援動態更新。
不過,proxy_pass http://c0.3.cn是支援動態網域名稱解析的。

2.5.2  備份上遊伺服器

upstream backend {

    server 192.168.61.1:9080  weight=1;

    server 192.168.61.1:9090  weight=2 backup;

}

9090連接埠上遊伺服器配置為備上遊伺服器,當所有主上遊伺服器都不存活時,請求會轉寄給備上遊伺服器。
如通過縮容上遊伺服器進行壓測時,要摘掉一些上遊伺服器進行壓測,但為了保險起見會配置一些備上遊伺服器,當壓測的上遊伺服器都掛掉時,流量可以轉寄到備上遊伺服器,從而不影響使用者請求處理。

2.5.3  不可用上遊伺服器

upstream backend {

    server 192.168.61.1:9080  weight=1;

    server 192.168.61.1:9090  weight=2 down;

}

9090連接埠上遊伺服器配置為永久不可用,當測試或者機器出現故障時,暫時通過該配置臨時摘掉機器。
2.6  長串連

配置Nginx與上遊伺服器的長串連,用戶端與Nginx之間的長串連可以參考位置“逾時與重試”的相應部分。
通過keepalive指令配置長串連數量。
upstream backend {

    server 192.168.61.1:9080  weight=1;

    server 192.168.61.1:9090  weight=2 backup;

   keepalive 100;

}
通過該指令配置了每個Worker進程與上遊伺服器可快取的空閑串連的最大數量。當超出這個數量時,最近最少使用的串連將被關閉。keepalive指令不限制Worker進程與上遊伺服器的總串連。
如果想要跟上遊伺服器建立長串連,則一定別忘了以下配置。

location / {

     #支援keep-alive

    proxy_http_version 1.1;

    proxy_set_header Connection "";

    proxy_pass http://backend;

}

如果是http/1.0,則需要配置發送“Connection: Keep-Alive”要求標頭。
上遊伺服器不要忘記開啟長串連支援。
接下來,我們看一下Nginx是如何?keepalive的(ngx_http_upstream_keepalive _module),擷取串連時的部分代碼。
ngx_http_upstream_get_keepalive_peer(ngx_peer_connection_t*pc, void *data) {

    //1.首先詢問負載平衡使用哪台伺服器(IP和連接埠)

    rc =kp->original_get_peer(pc, kp->data);
    cache =&kp->conf->cache;

    //2.輪詢 “空閑串連池”

    for (q =ngx_queue_head(cache);

         q!= ngx_queue_sentinel(cache);

         q =ngx_queue_next(q))

    {

        item = ngx_queue_data(q,ngx_http_upstream_keepalive_cache_t, queue);

        c =item->connection;

       //2.1.如果“空閑串連池”緩衝的串連IP和連接埠與負載平衡到的IP和連接埠相同,則使用此串連

        if (ngx_memn2cmp((u_char *)&item->sockaddr, (u_char *) pc->sockaddr,

                         item->socklen,pc->socklen)

           == 0)

        {

           //2.2.從“空閑串連池”移除此串連並壓入“釋放串連池”棧頂

           ngx_queue_remove(q);

           ngx_queue_insert_head(&kp->conf->free, q);

 

           goto found;

        }

    }

    //3.如果 “空閑串連池”沒有可用的長串連,將建立短串連

return NGX_OK;


釋放串連時的部分代碼。

ngx_http_upstream_free_keepalive_peer(ngx_peer_connection_t*pc, void *data, ngx_uint_t state) {

c = pc->connection;//當前要釋放的串連

    //1.如果“釋放串連池”沒有待釋放串連,那麼需要從“空閑串連池”騰出一個空間給新的串連使用(這種情況存在於建立串連數超出了串連池大小時,這就會出現震蕩)

    if(ngx_queue_empty(&kp->conf->free)) {

        q =ngx_queue_last(&kp->conf->cache);

        ngx_queue_remove(q);

        item= ngx_queue_data(q, ngx_http_upstream_keepalive_cache_t, queue);

       ngx_http_upstream_keepalive_close(item->connection);

    } else {//2.從“釋放串連池”釋放一個串連

        q =ngx_queue_head(&kp->conf->free);

       ngx_queue_remove(q);

        item= ngx_queue_data(q, ngx_http_upstream_keepalive_cache_t, queue);

    }

    //3.將當前串連壓入“空閑串連池”棧頂供下次使用

   ngx_queue_insert_head(&kp->conf->cache, q);

   item->connection = c;

總長串連數是“空閑串連池”+“釋放串連池”的長串連總數。首先,長串連配置不會限制Worker進程可以開啟的總串連數(超了的作為短串連)。另外,串連池一定要根據實際情境合理進行設定。

1.空閑串連池太小,串連不夠用,需要不斷建串連。
2.空閑串連池太大,空閑串連太多,還沒使用就逾時。
另外,建議只對小報文開啟長串連。

2.7  HTTP反向 Proxy樣本

反向 Proxy除了實現負載平衡之外,還提供如緩衝來減少上遊伺服器的壓力。
1.全域配置(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                   /tmpfs/proxy_temp;

proxy_cache_path                 /tmpfs/proxy_cache levels=1:2keys_zone                                  =cache:512m inactive=5m max_size=8g;

proxy_connect_timeout  3s;

proxy_read_timeout     5s;

proxy_send_timeout     5s;

開啟proxy buffer,緩衝內容將存放在tmpfs(記憶體檔案系統)以提升效能,設定逾時時間。
2.location配置
location ~ ^/backend/(.*)$ {

    #設定一致性雜湊負載平衡key

   set_by_lua_file $consistent_key "/export/App/c.3.cn/lua/lua_ balancing_backend.properties";

    #失敗重試配置

   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 web.c.3.local;

   proxy_pass http://backend /$1$is_args$args;

}

我們開啟了proxy_pass_request_body和proxy_pass_request_headers,禁止向上遊伺服器傳遞要求標頭和內容體,從而使得上遊伺服器不受要求標頭攻擊,也不需要解析;如果需要傳遞,則使用proxy_set_header按需傳遞即可。
我們還可以通過如下配置來開啟gzip支援,減少網路傳輸的資料包大小。

gzip                   on;

gzip_min_length     1k;

gzip_buffers         16 16k;

gzip_http_version   1.0;

gzip_proxied         any;

gzip_comp_level     2;

gzip_types          text/plainapplication/x-javascript text/css                            application/xml;

gzip_vary             on;

對於內容型響應建議開啟gzip壓縮,gzip_comp_level壓縮層級要根據實際壓測來決定(頻寬和輸送量之間的抉擇)。

備忘:文章中內容來源於網上資料整理,僅供參考。

Nginx負載平衡與反向 Proxy—《億級流量網站架構核心技術》

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.