新四季網

nginx負載均衡的幾種方式(一篇文章讓你搞懂)

2023-04-17 08:49:21 4

前面我們講了 Nginx 的 11 個階段以及各個模塊的用法,現在終於到了最重要也是最常用的一部分了,那就是反向代理和負載均衡,今天這篇文章介紹了負載均衡的原理以及對應的四種負載均衡算法,當然還有對應的指令及實戰,歡迎品嘗。

負載均衡

所謂負載均衡,就是 Nginx 把請求均勻的分攤給上遊的應用伺服器,這樣即使某一個伺服器宕機也不會影響請求的處理,或者當應用伺服器扛不住了,可以隨時進行擴容。

Nginx 在 AKF 可擴展立方體上的應用

在 x 軸上,可以通過橫向擴展應用伺服器集群,Nginx 基於 Round-Robin 或者 Least-Connected 算法分發請求。但是橫向擴展並不能解決所有問題,當數據量大的情況下,無論擴展多少臺服務,單臺伺服器數據量依然很大。在 y 軸上,可以基於 URL 進行不同功能的分發。需要對 Nginx 基於 URL 進行 location 的配置,成本較高。在 z 軸上可以基於用戶信息進行擴展。例如將用戶 IP 地址或者其他信息映射到某個特定的服務或者集群上去。

這就是 Nginx 的負載均衡功能,它的主要目的就是為了增強服務的處理能力和容災能力。

反向代理

反向代理和負載均衡在某種程度上是密不可分的。

Nginx 支持多種協議的反向代理。四層的反向代理比較簡單,無論是 UDP 還是 TCP 的流量過來,轉發到上遊的依然是 UDP 或 TCP 的流量。

而到了應用層時,就不太相同了,因為 HTTP 的 Header 中包含了大量的業務信息,需要根據 HTTP 的頭部轉換成不同的協議。

反向代理與緩存

緩存這個問題分為兩類,一類是時間緩存,一類是空間緩存。

時間緩存是指,當用戶請求一個頁面的時候,Nginx 發現沒有緩存,就會到後端伺服器去取,在返回給用戶響應的同時還會緩存一份,這樣當下一個用戶去請求的時候就會直接用緩存作為響應而不會再去請求上遊的伺服器。空間緩存這種用的比較少,主要是指當用戶發來請求的時候,Nginx 可以提前去上遊伺服器獲取一些響應的內容,這個後面可以看到是怎麼用的。

upstream 與 server 指令

指令name 表示負載均衡集群的名字,而 {} 內指定了一系列的伺服器server 後跟伺服器地址,地址後還可以加一些參數 parameters

Syntax: upstream name { ... }Default: —Context: httpSyntax: server address [parameters];Default: —Context: upstream

功能:指定一組上遊伺服器地址,地址可以是域名、IP 地址或者 Unix Socket 地址。可以在域名或者 IP 地址後加埠,如果不加埠,那麼默認使用 80 埠。通用參數:server 後可以添加的參數backup:指定當前 server 為備份服務,僅當非備份 server 不可用時,請求才會轉發到該 server表示某臺服務已經下線,不再服務

負載均衡算法

加權 Round-Robin 負載均衡算法

Round-Robin(rr) 負載均衡算法發給上遊伺服器的請求是輪詢發送的,相當於所有上遊伺服器根據順序依次處理髮來的請求。

有些情況下上遊伺服器性能不同,比如 4C8G 和 8C16G 的伺服器都有,那麼這時候就可以對伺服器設置一些權重,讓性能好的承擔更多的請求。

功能在加權輪詢的方式訪問 server 指令指定的上遊服務集成在 Nginx 的 upstream 框架中,無法移除指令weight:服務訪問的權重,默認是 1max_conns:server 的最大並發連接數,僅作用於單 worker 進程。默認是 0,表示沒有限制max_fails:在 fail_timeout 時間段內,最大的失敗次數。當達到最大失敗時,會在 fail_timeout 秒內這臺 server 不允許再次被選擇fail_timeout:單位是秒,默認 10 秒,可以指定一段時間內最大失敗次數 max_fails 以及到達 max_fails 之後該 server 不能訪問的時間

對上遊服務使用 keepalive 長連接

Nginx 與上遊服務一般是在內網中的,所以開啟 keepalive 後效果後更明顯。

功能:通過復用連接,降低 Nginx 與上遊伺服器建立、關閉連接的消耗,提升吞吐量的同時降低時延模塊: ngx_http_upstream_keepalive_module 默認編譯進 Nginx,通過 --without-http_upstream_keepalive_module 移除

對上遊伺服器的 HTTP 頭部設定

proxy_http_version 1.1;proxy_set_header Connection "";

upstream_keepalive 的指令

Syntax: keepalive connections;Default: —Context: upstream# 1.15.3 非穩定版本新增命令Syntax: keepalive_requests number;Default: keepalive_requests 100; Context: upstreamSyntax: keepalive_timeout timeout;Default: keepalive_timeout 60s; Context: upstream

keepalive connections;

指定上遊服務域名解析的 resolver 指令

當使用域名訪問上遊服務時,可以指定一個 DNS 解析的地址,還可以設置超時等,這個時候就要用到 resolver 指令。

Syntax: resolver address ... [valid=time] [ipv6=on|off];Default: —Context: http, server, locationSyntax: resolver_timeout time;Default: resolver_timeout 30s; Context: http, server, location

實戰

下面我起了兩個 Nginx 的進程,一個作為上遊伺服器,監聽 8011 和 8012 埠,另一個作為反向代理向上遊伺服器發請求。

上遊伺服器的配置如下,當請求是到達 8011 埠就返回 8011 server response. ,當請求到達 8012 埠返回 8012 server response. 。

server { listen 8011; default_type text/plain; return 200 '8011 server response.\n';}server { listen 8012; default_type text/plain; # client_body_in_single_buffer on; return 200 '8012 server response.\n';}

作為反向代理的 Nginx 伺服器配置是這個樣子的:

這裡面 8011 埠和 8012 埠的區別在於 8011 埠設置了權重和對應的參數。

upstream rrups { server 127.0.0.1:8011 weight=2 max_conns=2 max_fails=2 fail_timeout=5; server 127.0.0.1:8012; keepalive 32;}server { server_name rrups.ziyang.com; error_log myerror.log info; location /{ proxy_pass http://rrups; proxy_http_version 1.1; proxy_set_header Connection ""; }}

兩個 Nginx 都配置好之後,來測試一下:

➜ nginx curl rrups.ziyang.com8011 server response.➜ nginx curl rrups.ziyang.com8011 server response.➜ nginx curl rrups.ziyang.com8012 server response.

由於 8011 埠的權重設置的是 2,所以根據 rr 算法,每次都是先兩個連接負載到 8011 埠上然後是 8012 埠。

這一節講了 rr 負載均衡算法,rr 算法是所有負載均衡算法的基礎,在其他負載均衡算法失效的情況下,Nginx 也會使用 rr 算法進行負載均衡。

負載均衡哈希算法,ip_hash 與 hash 模塊

rr 輪詢算法沒有辦法保證請求由某一臺指定的伺服器去處理,只能輪詢處理請求,在 AKF 立方體中只能在 x 軸方向上進行水平擴展。如果基於 z 軸擴展,就可以採用哈希算法保證某一類請求只由特定的伺服器處理。

功能:以客戶端的 IP 地址作為 hash 算法的關鍵字,映射到特定的上遊伺服器中對 IPv4 地址使用前 3 個字節作為關鍵字,對 IPv6 則使用完整地址可以使用 rr 算法的參數可以基於 realip 模塊修改用於執行算法的 IP 地址模塊: ngx_http_upstream_ip_hash_module ,通過 --without-http_upstream_ip_hash_module 禁用模塊

指令的話比較簡單,就是 ip_hash 出現在 upstream 上下文中。

Syntax: ip_hash;Default: —Context: upstream

這裡面不得不提到的一個模塊就是 realip 模塊,哈希算法是根據 remote_addr 這個變量的值來進行哈希的,這個變量已經出現了好多次了,可見是多麼常用的一個變量。不熟悉的還是到前面Nginx 的 11 個階段 重新複習一下。

還有另外一個模塊 upstream_hash 模塊,這個模塊可以基於任意的關鍵字實現 hash 算法的複雜均衡。

基於任意關鍵字實現 hash 算法的負載均衡:upstream_hash 模塊

功能:通過指定關鍵字作為 hash key,基於 hash 算法映射到特定的上遊伺服器中關鍵字可以含有變量、字符串可以使用 rr 算法的參數模塊: ngx_http_upstream_hash_module ,通過 --without-http_upstream_ip_hash_module 禁用模塊

指令的話就是 hash 指令,後面可以跟關鍵字作為 key。

Syntax: hash key [consistent];Default: —Context: upstream

實戰

配置文件如下所示:

log_format varups '$upstream_addr $upstream_connect_time $upstream_header_time $upstream_response_time ' '$upstream_response_length $upstream_bytes_received ' '$upstream_status $upstream_http_server $upstream_cache_status';upstream iphashups { ip_hash; #hash user_$arg_username; server 127.0.0.1:8011 weight=2 max_conns=2 max_fails=2 fail_timeout=5; server 127.0.0.1:8012 weight=1;}server { set_real_ip_from 127.0.0.1; real_ip_recursive on; real_ip_header X-Forwarded-For; server_name iphash.ziyang.com; listen 80; error_log myerror.log info; access_log logs/upstream_access.log varups; location /{ proxy_pass http://iphashups; proxy_http_version 1.1; proxy_set_header Connection ""; }}

實際驗證一下,會發現不同的 ip 地址實際上是會被不同的上遊伺服器處理的,如果是同一個 ip 地址,那麼只會被一個上遊伺服器處理。

➜ nginx curl -H 'X-Forwarded-For: 10.200.20.20' iphash.ziyang.com8012 server response.➜ nginx curl -H 'X-Forwarded-For: 1.200.20.20' iphash.ziyang.com8011 server response.

基於 IP 或者基於自定義 key 的 hash 算法有一個嚴重的問題,那就是當上遊伺服器掛掉的話,Nginx 依然會向這臺伺服器發請求,這是因為,如果負載的不同的伺服器上去,可能會得到異常的響應,同時還可能導致大量的路由變更。下面的一致性哈希可以解決這個問題。

一致性哈希算法:hash 模塊

剛才說了基於 IP 的哈希算法存在一個問題,那就是當有一個上遊伺服器宕機或者擴容的時候,會引發大量的路由變更,進而引發連鎖反應,導致大量緩存失效等問題。那麼為什麼會造成這種情況呢?

假設我們基於 key 來做 hash,現在有 5 臺上遊伺服器,如果基於最簡單的 hash 算法對 key 取模,會將 key 和 server 一一對應起來。

當有一臺伺服器宕機的時候,就需要重新對 key 進行 hash,最後會發現所有的對應關係全都失效了,從而會引發緩存大範圍失效。

而一致性 hash 算法則可以解決這個問題。

一致性哈希算法的原理是,將一個環分成了 2^32 個區間範圍,四個節點將這個環劃分成為了四個區間,每個區間的請求都由對應的節點去處理。來看看當擴容的時候會發生什麼。

假設這時候發現 node4 負載過高,因此決定再添加一個節點進去分擔壓力,那麼影響的也只是這個節點之後的請求,可能會緩存失效,而其他的三個節點是不會有任何影響的。

這就是一致性 hash 算法的原理,一致性 hash 算法使用也很簡單,只需要將上一節指令中的參數打開即可:

Syntax: hash key [consistent];Default: —Context: upstream

這裡只需要指明 consistent 參數即可。

最少連接數算法

再來看一個最少連接數算法。這個算法顧名思義,它會優先選擇連接最少的上遊伺服器,是由 upstream_least_conn 模塊提供的。

功能:從所有上遊伺服器中,找出當前並發連接數最少的一個,將請求轉發到它如果出現多個最少連接伺服器的連接數都是一樣的,使用 rr 算法模塊: ngx_http_upstream_least_conn_module ,通過 --without-http_upstream_ip_hash_module 禁用模塊

指令的用法也很簡單,直接在 upstream 模塊中開啟 least_conn 指令即可。

Syntax: least_conn;Default: —Context: upstream

負載均衡策略對所有 worker 進程生效:upstream_zone 模塊

上面說的所有的負載均衡算法對於 worker 進程來說都是獨立的,每個 worker 進程之間並不互通,這樣在很多時候並不是我們期望的。

我們期望的應該是負載均衡算法對所有的 worker 進程生效。

功能:分配出共享內存,將其他 upstream 模塊定義的負載均衡策略數據、運行時每個上遊伺服器的狀態數據存放在共享內存上,以對所 Nginx worker 進程生效模塊: ngx_http_upstream_zone_module ,通過 --without-http_upstream_ip_hash_module 禁用模塊

一個指令,指定 zone 的名字以及對應的大小:

Syntax: zone name [size];Default: —Context: upstream

除此之外,各個負載均衡模塊之間是要遵循一定的順序的:

ngx_module_t *ngx_modules[] = { … … &ngx_http_upstream_hash_module, &ngx_http_upstream_ip_hash_module, &ngx_http_upstream_least_conn_module, &ngx_http_upstream_random_module, &ngx_http_upstream_keepalive_module, &ngx_http_upstream_zone_module, … …};

注意,這個模塊的順序是從上到下執行的,而不是我們前面過濾模塊的從下到上。

可以看到,zone 模塊在最後,也就是說,上面各個算法定義的參數和配置,最終 zone 模塊會把這些配置放到共享內存裡面生效。

這一節介紹了負載均衡的原理以及四種負載均衡算法,也可以說是三種,就是輪詢、哈希、最少連接數算法。每一種算法都有各自的應用場景,rr 算法是最基礎的負載均衡算法,在某些情況下其他算法失效的時候,會退化為 rr 算法。

upstream 提供的變量

先來介紹一組不含緩存的變量。

upstream_addr上遊伺服器的 IP 地址,格式為可讀的字符串,例如 127.0.0.1:8012upstream_connect_time與上遊服務建立連接消耗的時間,單位為秒,精確到毫秒upstream_header_time:這個接收時間是會影響到 Nginx 的性能的,因為只有接收了 Header 才能決定下一步如何處理接收上遊服務發迴響應中 HTTP 頭部所消耗的時間,單位為秒,精確到毫秒upstream_response_time接收完整的上遊服務響應所消耗的時間,單位為秒,精確到毫秒upstream_http_頭部從上遊服務返回的響應頭部的值upstream_bytes_received從上遊服務接收到的響應長度,單位為字節upstream_response_length從上遊服務返回的響應包體長度,單位為字節upstream_status上遊服務返回的 HTTP 響應狀態碼。如果未連接上,該變量值為 502upstream_cookie_名稱從上遊服務發回的響應頭 Set-Cookie 中取出的 cookie 值upstream_trailer_名稱從上遊服務的響應尾部取到的值

來看一下剛才的實戰中我們的例子。

在剛才的負載均衡實戰中有一條日誌的配置:

log_format varups '$upstream_addr $upstream_connect_time $upstream_header_time $upstream_response_time ' '$upstream_response_length $upstream_bytes_received ' '$upstream_status $upstream_http_server $upstream_cache_status';

這條配置用到了我們上面提到的很多變量,對應輸出的實際日誌長這個樣子:

127.0.0.1:8012 0.001 0.001 0.001 22 170 200 nginx/1.17.8 -

大家可以對照日誌格式看下分別代表什麼意思,這裡我就不細說了。

好了,今天這篇文章跟大家介紹了什麼是負載均衡,Nginx 主要是通過 upstream 模塊來提供對應的功能的,又介紹了負載均衡的四種算法,最後介紹了 upstream 中提供的變量。下一節課我們來說一說 Nginx 的反向代理。

,
同类文章
葬禮的夢想

葬禮的夢想

夢見葬禮,我得到了這個夢想,五個要素的五個要素,水火只好,主要名字在外面,職業生涯良好,一切都應該對待他人治療誠意,由於小,吉利的冬天夢想,秋天的夢是不吉利的
找到手機是什麼意思?

找到手機是什麼意思?

找到手機是什麼意思?五次選舉的五個要素是兩名士兵的跡象。與他溝通很好。這是非常財富,它擅長運作,職業是仙人的標誌。單身男人有這個夢想,主要生活可以有人幫忙
我不怎麼想?

我不怎麼想?

我做了什麼意味著看到米飯烹飪?我得到了這個夢想,五線的主要土壤,但是Tu Ke水是錢的跡象,職業生涯更加真誠。他真誠地誠實。這是豐富的,這是夏瑞的巨星
夢想你的意思是什麼?

夢想你的意思是什麼?

你是什​​麼意思夢想的夢想?夢想,主要木材的五個要素,水的跡象,主營業務,主營業務,案子應該抓住魅力,不能疏忽,春天夢想的吉利夢想夏天的夢想不幸。詢問學者夢想
拯救夢想

拯救夢想

拯救夢想什麼意思?你夢想著拯救人嗎?拯救人們的夢想有一個現實,也有夢想的主觀想像力,請參閱週宮官方網站拯救人民夢想的詳細解釋。夢想著敵人被拯救出來
2022愛方向和生日是在[質量個性]中

2022愛方向和生日是在[質量個性]中

[救生員]有人說,在出生88天之前,胎兒已經知道哪天的出生,如何有優質的個性,將走在什麼樣的愛情之旅,將與生活生活有什么生活。今天
夢想切割剪裁

夢想切割剪裁

夢想切割剪裁什麼意思?你夢想切你的手是好的嗎?夢想切割手工切割手有一個真正的影響和反應,也有夢想的主觀想像力。請參閱官方網站夢想的細節,以削減手
夢想著親人死了

夢想著親人死了

夢想著親人死了什麼意思?你夢想夢想你的親人死嗎?夢想有一個現實的影響和反應,還有夢想的主觀想像力,請參閱夢想世界夢想死亡的親屬的詳細解釋
夢想搶劫

夢想搶劫

夢想搶劫什麼意思?你夢想搶劫嗎?夢想著搶劫有一個現實的影響和反應,也有夢想的主觀想像力,請參閱週恭吉夢官方網站的詳細解釋。夢想搶劫
夢想缺乏缺乏紊亂

夢想缺乏缺乏紊亂

夢想缺乏缺乏紊亂什麼意思?你夢想缺乏異常藥物嗎?夢想缺乏現實世界的影響和現實,還有夢想的主觀想像,請看官方網站的夢想組織缺乏異常藥物。我覺得有些東西缺失了