返回部落格

Nginx HTTP 代理、負載平衡、緩衝與快取:概述

Nginx HTTP 代理、負載平衡、緩衝與快取:概述
簡介

Nginx 是一款高效能的網頁伺服器,也可用作反向代理、郵件代理、負載平衡器和 HTTP 快取。Nginx 是免費且開源的,允許任何人下載並在其伺服器環境中使用。

您可能已經使用過 Nginx 來提供網站服務。在本教學中,我們將討論 Nginx 的其他功能。HTTP 代理 功能允許它將請求傳遞給後端 HTTP 伺服器進行處理。利用此功能,您可以設定多個後端伺服器。它允許您根據需要擴充基礎架構,以處理激增的用戶端請求。

隨著教學的深入,您將學習如何使用 Nginx 的 負載平衡 屬性、緩衝 以及 快取 回應來提高伺服器效能,並確保為用戶端提供更好的體驗。讓我們開始吧!

首先,要開始使用 Nginx,請查看我們的 如何在 Ubuntu 伺服器上安裝 Nginx 的教學.

關於代理的一般資訊

如果您對網頁伺服器的了解僅限於處理網站請求和提供網頁,您可能會想知道為什麼我們需要代理請求。下面我們將解釋其背後的原因。

將請求從 Nginx 代理到其他伺服器的原因之一是為了支援基礎架構的可擴充性。Nginx 預設會同時處理許多連線。這使得它非常適合作為用戶端的第一個接觸點。然後,它可以將請求傳遞給各個後端伺服器,以處理用戶端請求的實際處理。這就是分擔負載的方式。因此,它確保您可以盡可能地擴充基礎架構。它還允許您關閉其他伺服器進行維護,而其他伺服器則繼續提供請求服務。

您可能想要將請求代理到其他伺服器的第二個原因,是當您使用的應用程式伺服器不適合在實際生產環境中直接處理來自用戶端的請求時。包括網頁伺服器在內的幾種框架並不適合像 Nginx 那樣的高效能。讓 Nginx 作為入口點並將請求代理到這些低效能的伺服器,可以確保您的使用者獲得更好的體驗。此外,它還可以保證提高應用程式的安全性。

在 Nginx 中代理請求的過程涉及處理來自 Nginx 伺服器的請求,並將其傳遞給其他後端伺服器進行實際處理。一旦其他後端伺服器處理了請求,它們就會將結果傳回給 Nginx。然後,它將結果作為回應傳送給用戶端。在這種情況下,用戶端是網頁瀏覽器,甚至是行動網頁應用程式。其他後端伺服器可以是網際網路上無法公開存取的本機伺服器、遠端伺服器,甚至是 Nginx 伺服器區塊(server blocks)設定中的其他虛擬伺服器。Nginx 代理請求的這些其他伺服器被稱為 上游伺服器.

Nginx 可以將請求代理到使用多種協定進行通訊的伺服器,包括 HTTP(S)、Memcached, SCGI, FastCGI、以及 uWSGI。對於每種協定類型,都有一組指令。我們本教學的重點是 HTTP 協定。Nginx 將請求和訊息元件解析為上游伺服器可以解釋和處理的格式。

分析基本的 HTTP 代理轉發

最簡單的代理類型涉及將請求傳遞給透過 HTTP 進行通訊的單一伺服器。這種類型的代理通常被稱為「代理轉發」(proxy pass),並由 Nginx 設定檔中恰如其名的 proxy_pass 指令處理。

proxy_pass 指令出現在 location 區塊中。它也位於 location 上下文的區塊中以及 limit_except 上下文中。當請求與包含 proxy_pass 指令的 location 匹配時,該請求將轉到該指令指定的 URL。以下是設定程式碼片段的範例:

proxy_pass_conf

在上述範例中,對連接埠 80 的請求將會傳送到 localhost:3000:

nginx default page

上方的螢幕截圖顯示了當您嘗試存取 localhost 時的預設 Nginx 頁面。在重新啟動啟用 proxy pass 指令的 Nginx 伺服器後,所有請求都將傳送到連接埠 3000。一個示範應用程式正在連接埠 3000 上執行,您可以從下圖中看到,並且可以直接從 localhost 存取而無需指定連接埠:

localhost after applying proy pass

在下一個範例中,在 proxy_pass 定義中的伺服器末尾沒有指定 URI。對於符合此模式的定義,用戶端請求的 URI 將原封不動地傳遞給上游伺服器。

例如,當此區塊處理對 /match/url/here 的請求時,請求 URI 將以 http://example.com/match/url/here 的形式傳送到 example.com 伺服器。

以下是替代設定程式碼片段的範例:

如您在上述程式碼片段中所見,我們在代理伺服器的末尾定義了一個 URI 片段為 new/url/prefix。當您在 proxy_pass 定義中定義 URI 時,在傳送到上游伺服器進行處理時,請求中與 location 定義相符的部分將被此 URI 取代。

例如,對 Nginx 伺服器上 /match/url/here 的請求會以 http://example.com/new/url/here 的形式傳遞給上游伺服器。/match/url 會被替換為 /new/url。請記住這一點。

在某些情況下,無法像上述那樣傳遞 URI。在這種情況下,Nginx 會忽略 proxy_pass 定義末尾的 URI。最終,來自用戶端的原始 URI 或其他指令修改後的 URI 將被傳遞給上游伺服器。

例如當使用正規表示式比對 location 時。Nginx 可能無法確定 URI 的哪一部分與該表示式相符。因此,它會傳送原始的用戶端請求 URI。這會導致在同一個區塊中重寫和處理用戶端 URI。在這種情況下,將會傳遞重寫後的 URI。

Nginx 如何處理標頭?

標頭對於伺服器如何處理請求至關重要。某些標頭可能包含驗證資訊。因此,我們必須了解 Nginx 代理將如何處理標頭。從 Nginx 到上游伺服器的代理請求看起來會與直接來自用戶端的請求不同。其中一些差異是伴隨代理請求發送的標頭所導致的。

在代理請求的過程中,Nginx 會對其從用戶端接收到的請求標頭進行調整。其中一些調整包括:

  • 清除所有空標頭。空標頭只會膨脹請求,因此沒有必要將它們傳遞給上游伺服器。

  • 預設情況下,任何包含底線的標頭都被視為無效,因此會從請求中移除。如果您想變更此行為並允許 Nginx 將包含底線的標頭視為有效,則可以將 underscores_in_headers 指令設定為「on」。如果您不這樣做,那麼來自用戶端的此類標頭將永遠無法到達上游伺服器。

  • 「Host」標頭會被重寫為 $proxy_host 變數指定的值。這是由 proxy_pass 指令指定的上游伺服器的 IP 地址或名稱以及連接埠號碼。

  • 「Connection」標頭值會變更為「close」。Connection 標頭保存了關於兩方之間建立的特定連線的資訊。當 Nginx 將其值設定為 close 時,它會向上游伺服器指示,一旦原始請求得到回應,連線就會關閉,因此不應期望它是一個持續連線。

以下是我們可以從上述代理請求標頭調整中注意到的一些要點:

  • 如果您不希望將標頭傳遞給上游伺服器,則將其設置為空字串將會完全從請求中移除該標頭。

  • 如果上游伺服器中的應用程式將處理非標準標頭,請確保標頭中不包含底線。或者,您可以在配置中將 underscores_in_headers 指令設置為「on」(這在 IP 地址/連接埠組合的預設伺服器宣告上下文中,或在 HTTP 上下文中均有效)。這樣做將確保標頭不會被標記為無效,從而能實際傳遞給上游伺服器。

  • 在大多數代理情況下,「Host」標頭非常重要。預設情況下,它被設置為 $proxy_host 的值,該變數包含從 proxy_pass 規格中檢索到的網域名稱或 IP 地址和連接埠。此地址是預設選擇的,並直接從連線資訊中提取。這是 Nginx 唯一能保證上游伺服器會做出回應的地址。

以下是「Host」標頭最常用的值:

  • $host – 按照優先順序設置的變數,依次為:請求行本身的執行主機名稱、來自用戶端請求的「Host」標頭,或與請求相匹配的伺服器名稱。

  • $http_host – 將「Host」標頭設置為用戶端請求中的「Host」標頭的變數。用戶端請求中的標頭在 Nginx 中始終可以作為變數使用。這些變數以 $http_ 前綴開頭,後跟小寫的標頭名稱。雖然 $http_host 變數在大多數情況下都能正常工作,但當用戶端請求缺少有效的「Host」標頭時,可能會導致傳遞失敗。

  • $proxy_host – 將「Host」標頭設置為從 proxy_pass 規格中檢索到的網域名稱或 IP 地址和連接埠組合的變數。從 Nginx 的角度來看,這是預設行為,因此被認為是安全的。然而,這可能不是伺服器正確處理請求所需要的。

大多數配置都會將「Host」標頭設置為 $host 變數。它非常靈活,並能為上游伺服器提供精確填寫的標頭。

設置和修改標頭

proxy_set_header 指令允許我們為代理連線設置或修改標頭。在前面討論的「Host」標頭中,我們可以執行以下操作來修改和添加代理請求中常見的其他標頭:

在上述配置片段中,我們將「Host」標頭設置為 $host 變數,該變數包含有關所請求的原始主機的資訊。我們設置了 X-Forwarded-Proto 標頭,其中包含有關來自用戶端的原始請求協定方案 (schema) 的資訊(這可以是 HTTP 或 HTTPS 請求)。

我們將用戶端的實際 IP 地址傳遞給 X-Real-IP。這使上游伺服器能夠根據用戶端的 IP 來源做出適當的決策或儲存記錄。X-Forwarded-For 標頭包含在到達此處之前,用戶端已被代理通過的所有伺服器的 IP 地址列表。在上面的程式碼片段中,我們將其設置為 $proxy_add_x_forwarded_for 變數。該變數將採用從用戶端獲取的原始 X-Forwarded-For 標頭的值,並將 Nginx 代理伺服器的 IP 地址添加到末尾。

如果您希望在多個 location 中引用 proxy_set_header 指令,可以將其移至 server 或 http 上下文中。請參考下面的配置片段:

定義 Upstream 上下文以進行代理連線的負載平衡

至此,您已經了解如何對單個後端 upstream 伺服器進行簡單的 http 代理。幸運的是,使用 Nginx,您可以透過定義後端伺服器池來傳遞請求以進行處理,從而擴展此類配置。

Nginx 提供了一個名為 upstream 的指令,用於定義伺服器池。在該指令的配置中,您只需指定能夠處理用戶端請求的伺服器。Nginx 作為代理伺服器,允許以最少的精力擴展基礎架構。upstream 指令必須在 Nginx 配置的 http 上下文中指定。

以下是顯示 upstream 指令的範例:

在上面的配置程式碼片段中,我們定義了一個名為 several_backend_hosts 的 upstream 上下文。定義的上下文名稱現在可以在 proxy_pass 中使用。它可以像常規網域一樣使用,如範例所示。在 server 區塊中,我們將所有對 example.com/proxy-me/… 的請求傳遞給我們使用 upstream 指令定義的伺服器池,在此範例中為 several_backend_hosts。系統會透過套用可配置的演算法,在伺服器池中選擇一個主機來處理傳入的請求。預設情況下,選擇遵循 輪詢 (round-robin) (循環)程序 – 每個請求都會輪流路由到不同的主機。

如何變更 Upstream 負載平衡演算法

如上所述,選擇程序遵循輪詢程序。在本節中,我們將介紹如何修改 upstream 伺服器池所使用的負載平衡演算法。要修改演算法,您可以在 upstream 上下文中包含其他指令或標記,如下所示:

  • (round-robin) – 如果未指定其他 upstream 負載平衡指令,則預設情況下,在 upstream 上下文中定義的每個伺服器都會依序接收傳遞的請求。

  • least_conn – 此指令指示 upstream 選擇活動連線數最少的後端伺服器。這適用於與某個後端伺服器的連線可能會持續一段時間的情況。

  • hash – 此指令常用於 memcached 代理。連線會根據隨機提供的雜湊鍵 (hash key) 的值傳遞給後端伺服器。雜湊鍵的值可以是變數、文字或兩者的組合。hash 恰好是唯一需要使用者輸入作為雜湊鍵的負載平衡方法。

  • ip_hash – 此指令指示 upstream 根據用戶端的 IP 地址將請求分配給不同的伺服器。IP 地址的前三個八位元組是決定由哪台伺服器處理請求的關鍵。此指令的優點是用戶端每次往往會連線到同一台伺服器,從而確保工作階段 (session) 的一致性。

以下是如何將負載平衡演算法指令新增至 upstream 上下文的範例:

在上述程式碼片段中,Nginx 將選擇連線數最少的任何一部伺服器來處理傳入的請求。ip_hash 指令遵循相同的語法。對於 hash 指令,您必須提供一個自訂的鍵值來進行雜湊,以下是一個範例:

這裡使用的雜湊將是客戶端’的 IP 位址和連接埠的結果。選用參數 consistent 實作了 ketama 一致性雜湊演算法。這可以確保在您變更上游伺服器時,對快取的影響降到最低。

如何指定伺服器權重以進行負載平衡

預設情況下,當您宣告後端伺服器時,每部伺服器的權重是相同的。其假設是每部伺服器都擁有處理相同負載量的資源和能力,當然,這是在考慮您在 upstream 上下文中指定的任何平衡演算法的情況下。若要變更此預設行為,您可以在宣告期間為每部伺服器設定不同的權重。讓我們來看一個範例:

在此範例中,host1.example.com 將接收其他兩部伺服器兩倍的流量。每部伺服器的預設權重為 1。

使用緩衝區釋放後端伺服器

在伺服器設定中設定代理時,您可能會擔心增加更多伺服器對效能造成的影響。幸運的是,Nginx 提供了緩衝和快取功能,可以幫助減輕這些效能問題。

在代理到另一部伺服器時,兩種不同連線的速度肯定會影響用戶端的體驗:

  • 第一種連線是從用戶端到 Nginx 代理。

  • 第二種連線是從 Nginx 代理到後端上游伺服器。

Nginx 可以根據需要調整其行為,以協助最佳化其中任何一種連線。

如果我們移除緩衝區,來自上游後端的資料會立即在 Nginx 代理處開始傳輸給用戶端。如果您知道您的用戶端速度很快,您可以完全關閉緩衝,以確保資料足夠快地到達用戶端。當您開啟緩衝時,Nginx 代理會暫時儲存從後端上游伺服器接收到的回應資料。然後,它會根據用戶端的速度將資料傳送給用戶端。一旦 Nginx 的緩衝區中有了回應,它就可以關閉與後端伺服器的連線。然後,它會以用戶端支援的速度將資料分發給用戶端。同時,這也允許後端伺服器繼續處理其他傳入的請求。

預設情況下,Nginx 會開啟緩衝。這是因為我們無法得知用戶端的連線速度。用戶端的連線通常各不相同,且可能較慢。下面,我們將定義可用於調整 Nginx 緩衝行為的各種指令。這些指令可以在 http、server 或 location 上下文中定義,但是,您應該注意,大小調整指令是針對每個請求進行設定的。因此,在有太多傳入的用戶端請求時,將它們增加到超出絕對必要的範圍可能會影響伺服器的效能。以下是這些指令:

  • proxy_buffering – 控制特定上下文及其子上下文是否啟用緩衝的指令。proxy_buffering 的預設配置為「on」。

  • proxy_buffer_size – 指定用於儲存從後端伺服器回應中取得的標頭(headers)的緩衝區大小。標頭構成了後端伺服器回應的第一部分。這些標頭的緩衝是與回應的其餘部分分開的。預設情況下,此緩衝區的設定大小與 proxy_buffers 相同。然而,如果標頭資訊很小,您可以將其大小設定為較低的值。

  • proxy_buffers – 控制代理回應的緩衝區數量(第一個參數)和大小(第二個參數)的指令。預設配置指定 8 個大小等於一個記憶體分頁(4k 或 8k)的緩衝區。您可以透過增加緩衝區的數量來允許緩衝更多資訊。

  • proxy_max_temp_file_size – 指定每個請求在磁碟上暫存檔案的最大大小的指令。當上游回應太大而無法放入緩衝區時,就會建立暫存檔案。

  • proxy_busy_buffers_size – 指定可傳送為「用戶端就緒」(即忙碌)狀態的緩衝區最大大小的指令。用戶端一次只能從一個緩衝區讀取數據。然而,緩衝區會在佇列中批次傳送給用戶端。您可以透過修改此指令來指定允許處於此狀態的緩衝區空間大小。

  • proxy_temp_file_write_size – 指定當來自後端上游伺服器的回應太大而無法放入已配置的緩衝區時,Nginx 一次寫入暫存檔案的數據量的指令。

  • proxy_temp_path – 指定當來自上游後端伺服器的回應太大而無法放入已配置的緩衝區時,Nginx 應在磁碟上儲存任何暫存檔案的路徑的指令。

Nginx 具有高度可客製性,為您提供了多個指令來微調緩衝行為。在大多數情況下,預設值就能很好地運作。同時,了解您可以針對自訂實作調整其中一些值也是件好事。您通常會想要調整 proxy_buffers 和 proxy_buffer_size 指令。

以下是一個增加每個上游請求可用代理緩衝區數量的範例。它在增加數量的同時,減少了儲存標頭的緩衝區大小:

讓我們來看看如何透過完全關閉緩衝,更快速地向快速用戶端傳送數據。如果您的用戶端不夠快,Nginx 將會自動使用緩衝區。然而,它會先將數據直接傳送給用戶端,而不是等待緩衝池。這種配置有一個缺點:它會導致與慢速用戶端的上游伺服器連線保持開啟,直到用戶端接收完所有回應數據。如果緩衝設定為「off」,則只會使用由 proxy_buffer_size 指令定義的緩衝區。以下是顯示如何指定關閉緩衝的程式碼片段:

  • 配置高可用性 (HA) 基礎架構(選用設定)

您可以在 Nginx 代理設定中新增一組備用的負載均衡器,以確保其更加穩健,從而實現高可用性。高可用性 (HA) 架構是一種沒有單點故障的基礎設施。負載均衡器是此設定的一部分。透過多個負載均衡器,您可以防止在某個負載均衡器故障或因維護而離線時可能發生的停機時間。

如何實作 Nginx 代理快取以降低回應時間

在上一節中,我們討論了如何使用緩衝區來釋放後端伺服器以處理更多請求。Nginx 還提供了另一個功能,允許我們快取來自後端的回應資料。它完全免除了為所有傳入請求連接上游伺服器的需要。

實作代理快取

proxy_cache_path 指令允許我們透過指定磁碟上的區域來設定快取,以用於儲存代理內容。proxy_cache_path 指令是在 http 上下文中定義的。

以下設定程式碼片段是您如何實作快取系統的範例:

在此程式碼片段中,我們使用了 proxy_cache_path 指令來定義檔案系統上保存快取的目錄。在此範例中,我們設定的目錄是 /var/lib/nginx/cache。您可以自由定義您選擇的目錄路徑。使用以下指令來建立您選擇的目錄,並設定正確的權限和所有權:

在程式碼片段中,levels= 參數指定了快取的組織方式。Nginx 將透過對鍵值(使用 proxy_cache_key 指令指定)進行雜湊處理來建立快取鍵。我們指定的 levels (1:2) 表示將建立一個單字元目錄(即雜湊值的最後一個字元)以及一個雙字元子目錄(取自雜湊值倒數第二和第三個字元)。在大多數情況下,您不需要關心這個。然而,了解它如何幫助 Nginx 快速找到相關值,這’是件好事。

keys_zone= 參數定義了快取區域的名稱,在我們的範例中,我們將其命名為 backendcache。在這裡,我們還定義了想要儲存多少中介資料。在此範例中,我們儲存了 8 MB 的鍵。Nginx 每個百萬位元組 (MB) 大約可以儲存 8000 個項目。max_size 參數指定了實際快取資料的最大大小,在我們的範例中為 50MB。

您還應該注意所使用的 proxy_cache_key 指令。此指令指定了我們將用於儲存快取值的鍵。我們將使用相同的鍵來檢查請求是否存在於快取中。我們已將該鍵指定為配置協定 (http 或 https)、HTTP 請求方法以及請求的主機和 URI 的組合。

此外,我們使用了 proxy_cache_valid 指令。此指令可以針對各種狀態碼多次指定。它允許我們根據狀態碼指定儲存值的時間。在程式碼片段中,我們為成功狀態碼指定了 10 分鐘,為 404 回應指定了 1 分鐘。

既然我們已經設定了快取區域,下一步就是透過告訴 Nginx 何時使用快取來使該設定生效。以下是顯示我們如何實作使用此快取的設定片段:

在 proxy_cache 指令中,我們已指定在此上下文中應使用 backendcache 快取區域。如果您在快取設定中選擇了不同的名稱,可以在此處進行替換。對於每個有效的項目,Nginx 都會在將請求傳遞給後端上游伺服器之前檢查快取。

我們定義了 proxy_cache_bypass 指令以使用 $http_cache_control 變數。此變數會告訴伺服器應該以快取的回應還是全新的、未快取的資源版本進行回應。正確設定此指令可讓 Nginx 正確處理來自用戶端的各種類型的傳入請求。

同時也指定了一個名為 X-Proxy-Cache 的額外標頭。此標頭的值為 $upstream_cache_status 變數。它為我們提供有關請求是導致快取命中、快取未命中,還是快取被明確繞過的資訊。此類資訊對用戶端非常有用,且在應用程式偵錯期間至關重要。

關於快取結果的重要注意事項

雖然快取將大大提高代理伺服器的效能,但在實作快取時,您應注意以下事項:

任何與使用者’個人資訊相關的資料都不應被快取,以避免某個使用者的資料被另一個使用者看見的情況。

您的後端伺服器應考慮到您網站的所有動態元素。我們可以在回應中指定多個 Cache-Control 標頭以達到不同的目的。讓我們來討論它們:

  • no-cache – 指定代理伺服器在提供回應之前,必須檢查後端上的資料是否已變更。這適用於動態且重要的資料。每次請求都會檢查 ETag 雜湊中介資料標頭,如果後端傳回相同的雜湊值,則提供先前的值。

  • no-store – 指定不對接收到的任何資料進行快取,因此,每個請求都將傳送到伺服器以獲取最新資料。這對於敏感資料是最安全的。

  • private – 指定任何共用快取空間都不應快取該資料。您可以使用此標頭指定在使用者瀏覽器中進行快取,但同時通知代理伺服器將該資料視為對後續請求無效。

  • public – 指定公開回應,並允許在連線中的任何點進行快取。

您可以使用 max-age 標頭指定您希望快取持續的時間(以秒為單位)。上面定義的各種標頭可以幫助您實作快取,同時確保敏感資料的安全、動態資料的新鮮,最重要的是,提高您伺服器’的效能。

如果您的後端伺服器正在執行 Nginx 伺服器,您可以在 server 區塊中指定快取的有效時間。您可以透過在設定中新增 expires 指令來執行此操作,如下所示:

第一個區塊允許將內容快取 59 分鐘,而第二個區塊則表示不進行快取。這些設定適用於 Cache-Control 標頭選項,例如第二個區塊的 “no-cache”。

您可以使用 add-header 指令來設定額外的值:

結論

在本教學中,我們學習了 Nginx 的強大功能。Nginx 既是網頁伺服器,最重要的是,它也是一個反向代理。Nginx 的設計使其能夠處理數千個並行連線。這使其非常適合用於負載平衡。由於這種設計,將請求代理到其他後端伺服器進行處理是非常簡單直接的。

憑藉本教學中的知識,得益於 Nginx 的靈活性,您應該能夠實作複雜的代理和負載平衡器。

以下是您可以在我們的部落格上找到的一些資源,可以讓您進一步了解 Nginx:

祝您使用愉快!

author

Pranay Kapgate

作者 · CloudSigma

Preslav Dobrev 是 CloudSigma 的創意設計師,專注於透過傳統與創新行銷渠道建立一致的企業形象。他擅長將藝術願景與策略行銷相融合,創造具有影響力的品牌敘事。

留言

目前尚無留言。成為第一個留言的人吧。