使用NGINX 和NGINX Plus 實現負載平衡(第2 部分)

本文將介紹NGINX Plus 具備的一些其他功能:透過keepalive、健康檢查、會話保持、重新導向及內容重寫來優化效能。

《使用NGINX 和NGINX Plus 實現負載平衡(第1 部分)》中,我們設定了一個簡單的HTTP 代理程式來跨多台Web 伺服器對流量進行負載平衡。 本文將介紹NGINX Plus 具備的一些其他功能:透過keepalive、健康檢查、會話保持、重新導向及內容重寫來優化效能。

編按-NGINX Plus Release 5 及進階版本還可以對基於TCP 的應用進行負載平衡。Release 6 透過增添健康檢查、動態重新配置、SSL 終止等功能,顯著擴展了TCP 負載平衡。在NGINX Plus Release 7 及進階版本中,TCP 負載平衡器具備與HTTP 負載平衡器相同的功能。Release 9 中引入了對UDP 負載平衡的支援。

您可以在stream 上下文(而非http 上下文)中設定TCP 和UDP 負載平衡。 由於HTTP 和TCP/UDP 之間的固有差異,因此可用指令和參數略有不同。

快速回顧

回顧一下,這是我們在上一篇文章中建立的配置:

server {
    listen 80;

    location / {
        proxy_pass http://backend;

        # Rewrite the 'Host' header to the value in the client request,
        # or primary server name
        proxy_set_header Host $host;

        # Alternatively, put the value in the config:
        # proxy_set_header Host www.example.com;
    }
 }

upstream backend {
    zone backend 64k;  # Use NGINX Plus' shared memory
    least_conn;

    server webserver1 weight=1;
    server webserver2 weight=4;
}

本文將介紹一些配置NGINX 和NGINX Plus 的簡單方法,以提高負載平衡效率。

HTTP keepalive

在NGINX 或NGINX Plus 與上游伺服器之間啟用HTTP keepalive 可提高效能(透過減少延遲),並降低NGINX 耗盡臨時連接埠的可能性。

HTTP 協定使用底層TCP 連線來傳送HTTP 請求並接收HTTP 回應。 HTTP keepalive 連接允許重複使用這些TCP 連接,從而避免了為每個請求建立和終止連接的開銷:

Edzcjqvueslqserwfcwx903nwv6byhr7aumryuy5

NGINX 是一個全代理,可獨立管理客戶端連線(前端keepalive 連線)和伺服器連線(上游keepalive 連線):

Zpypeewhla4ijbexpqc6w3aiw52m02cexswhkx6o

NGINX 會維護keepalive 連接的「快取」(一組與上游伺服器的空閒keepalive 連接),當需要將請求轉發到上游伺服器時,它會使用這些快取中已建立的keepalive 連接,而非創建新的TCP 連接。

這可減少NGINX 與上游伺服器之間的事務延遲,並降低臨時連接埠的使用率,因此NGINX 能夠處理大量流量並對其進行負載平衡。當流量激增時,這些快取會被清空,在這種情況下,NGINX 將與上游伺服器建立新的HTTP 連線。

對於其他負載平衡工具,該技術有時稱為「多路復用」、「connection pool」、「連接多工」或「OneConnect」。

您可透過將proxy_http_version、proxy_set_header 及keepalive 指令加入設定中來設定keepalive 連線快取:

server {
    listen 80;
    location / {
        proxy_pass http://backend;
        proxy_http_version 1.1;
        proxy_set_header Connection "";
     }
}

upstream backend {
    server webserver1;
    server webserver2;

    # maintain up to 20 idle connections to the group of upstream servers
    keepalive 20;
}

健康檢查

啟用健康檢查不僅能夠提高負載平衡服務的可靠性,降低最終用戶出錯率,而且還便於執行常見維護操作。

NGINX Plus 的健康檢查功能可用於偵測上游伺服器的故障。 NGINX Plus 使用「綜合事務」來偵測每台伺服器,並根據您在health_check 指令中配置的參數(以及新增match 參數的情況下,關聯的match 配置區塊)檢查回應:

server {
    listen 80;

    location / {
        proxy_pass http://backend;

        health_check interval=2s fails=1 passes=5 uri=/test.php match=statusok;

        # The health checks inherit other proxy settings
        proxy_set_header Host www.foo.com;
    }
}

match statusok {
    # Used for /test.php health check
    status 200;
    header Content-Type = text/html;
    body ~ "Server[0-9]+ is alive";
}

健康檢查從上層location 區塊繼承一些參數。如果在配置中使用運行時變量,這可能會導致出現問題。例如,下列設定適用於實際HTTP 流量,因為它從客戶端請求中提取Host 請求頭的值。但此配置不適用於健康檢查所使用的綜合事務,因為未對這些事務設定Host 請求頭,這表示綜合事務中沒有使用Host 請求頭。

location / {
    proxy_pass http://backend;

    # This health check might not work...
    health_check interval=2s fails=1 passes=5 uri=/test.php match=statusok;

    # Extract the 'Host' header from the request
    proxy_set_header Host $host;
}

一個好方法是建立一個虛擬location 區塊,以靜態定義健康檢查事務使用的所有參數:

location /internal-health-check1 {
    internal; # Prevent external requests from matching this location block

    proxy_pass http://backend;

    health_check interval=2s fails=1 passes=5 uri=/test.php match=statusok;

    # Explicitly set request parameters; don't use run-time variables
    proxy_set_header Host www.example.com;
}

會話保持

透過會話保持,可以讓無法部署至叢集的應用也能實現負載平衡和可靠擴展。儲存和複製會話狀態的應用程式可以更有效率地運行,幫助提升最終用戶效能。

某些應用程式有時會將狀態資訊儲存到上游伺服器,例如當使用者將商品放入虛擬購物車或編輯上傳的圖片時。在這些情況下,您可能想要將來自該使用者的所有後續請求都導向到同一台伺服器。

會話保持指定了請求所須路由到的目標位置,而負載平衡則允許NGINX 自由選擇最佳上游伺服器。透過NGINX Plus 的會話保持功能,這兩個進程可以共存:

如果請求符合會話保持規則
那麼使用目標上游伺服器
否則應用負載平衡演算法選擇上游伺服器

如果會話保持決策因目標伺服器不可用而失敗,那麼NGINX Plus 會做出負載平衡決策。

最簡單的會話保持方法是「sticky cookie」方法,其中NGINX Plus 在第一個回應中插入一個cookie,用於識別sticky 上游伺服器:

sticky cookie srv_id expires=1h domain=.example.com path=/;

在另一種「sticky 路由」方法中,NGINX 會根據JSESSIONID cookie 等請求參數選擇上游伺服器:

upstream backend {
   server backend1.example.com route=a;
   server backend2.example.com route=b;

    # select first non-empty variable; it should contain either 'a' or 'b'
    sticky route $route_cookie $route_uri;
}

重寫HTTP 重定向

如果某些重定向被破壞,則您需要重寫HTTP 重定向,特別是當您從代理程式被重定向到真正的上游伺服器時。

當您代理到上游伺服器時,伺服器會在本機位址上發布應用,但您透過另一個位址(代理的位址)存取應用程式。這些地址通常解析為域名,如果伺服器和代理的域名不同,就會出現問題。

例如,在測試環境中,您可能直接(透過IP 位址)或按localhost 對代理程式進行尋址。但上游伺服器可能會監聽真實網域(例如www.nginx.com)。當上游伺服器發出重定向訊息(使用3xx 狀態碼和Location 請求頭,或使用Refresh 請求頭)時,訊息中可能包含伺服器的真實網域。

NGINX 會嘗試攔截並修正這種最常見的問題。如果您需要全權控制以執行特定重寫,請使用proxy_redirect 指令,如下所示:

proxy_redirect http://staging.mysite.com/ http://$host/;

重寫HTTP 回應

有時,您需要重寫HTTP 回應中的內容。也許,如上例所示,回應中包含指向代理以外其他伺服器的絕對連結。

您可以使用sub_filter 指令來定義要套用的重寫:

sub_filter /blog/ /blog-staging/;
sub_filter_once off;

一個很常見的問題是HTTP 壓縮的使用。如果客戶端發出訊號表示可以接受壓縮數據,而伺服器隨後壓縮了回應,那麼NGINX 就無法檢查和修改回應。最簡單的方法是將Accept-Encoding 請求頭設定為空字串(“”),從而將其從客戶端請求中刪除:

proxy_set_header Accept-Encoding "";

完整範例

下面是一個負載平衡配置模板,它使用了本文提到的所有方法。

[編者按-以下配置已更新為使用NGINX Plus API 對上游群組進行即時活動監控和動態配置,取代了最初使用的單獨模組。 ]

server {
    listen 80;

    location / {
        proxy_pass http://backend;

        proxy_http_version 1.1;
        proxy_set_header Connection "";
        proxy_set_header Accept-Encoding "";
        proxy_redirect http://staging.example.com/ http://$host/;

        # Rewrite the Host header to the value in the client request
        proxy_set_header Host $host;

        # Replace any references inline to staging.example.com
        sub_filter http://staging.example.com/ /;
        sub_filter_once off;
    }

    location /internal-health-check1 {
        internal; # Prevent external requests from matching this location block
        proxy_pass http://backend;
        health_check interval=2s fails=1 passes=5 uri=/test.php match=statusok;
        # Explicitly set request parameters; don't use runtime variables
        proxy_set_header Host www.example.com;
    }
 
upstream backend {
    zone backend 64k; # Use NGINX Plus' shared memory
    least_conn;
    keepalive 20;

    # Apply session persistence for this upstream group
    sticky cookie srv_id expires=1h domain=.example.com path=/servlet;

    server webserver1 weight=1; 
    server webserver2 weight=4; 
}

match statusok {
    # Used for /test.php health check
    status 200;
    header Content-Type = text/html;
    body ~ "Server[0-9]+ is alive";
}

server {
    listen 8080;
    root /usr/share/nginx/html;

    location = /api {
        api write=on; # Live activity monitoring and
                      # dynamic configuration of upstream groups

        allow 127.0.0.1; # permit access from localhost
        deny all;        # deny access from everywhere else
    }
}

若要試用NGINX Plus 中所有出色的負載平衡功能,請點擊此處立即下載30 天免費試用版

文章來源:NGINX