Nginx - 限制並發、限制訪問速率、限制流量


 

1. 前言

  本文針對 Nginx 的三個模塊進行配置,並證實各自的功能特點:

  (1)limit_conn_zone 模塊  - 限制同一 IP 地址並發連接數;

  (2)limit_request 模塊 - 限制同一 IP 某段時間的訪問量;

  (3)core 模塊提供 - limit_rate 限制同一 IP 流量。

  在 Nginx 中 以 LIMIT 開頭的 配置項,都是做 限制 功能,以上三個功能都是 Nginx 編譯后就有的功能,屬於內置模塊。

 

2. limit_conn_zone 模塊

  通過 limit_zone 模塊來達到限制用戶的連接數的目的,即限制同一用戶 IP 地址的並發連接數

 

2.1 配置示例

 

 

2.2 指令

指令名稱:limit_conn_zone

(nginx 1.18以后用 limit_conn_zone 取代了 limit_conn)

語法:limit_conn_zone key zone=name:size;

默認:no

區域:http

功能:該指令定義一個 zone,該 zone 存儲會話的狀態。

例如:上面的例子中,$binary_remote_addr  是 獲取客戶端ip地址的變量,長度為 4 字節,會話信息的長度為 32 字節。

 

指令名稱:limit_conn

語法:limit_conn zone number;

默認:no

區域:http、server、location

功能:該指令用於為一個會話設定最大並發連接數。如果並發請求超過這個限制,那么將返回預定錯誤(limit_conn_status )

 

指令名稱:limit_conn_status

語法:limit_conn_status code;

默認:limit_conn_status 503;

區域:http、server、location

功能:設置要返回的狀態碼以響應被拒絕的請求。

 

指令名稱:limit_conn_log_level

語法:limit_conn_log_level info | notice | warn | error

默認值:error

區域:http、server、location

功能:該指令用於設置日志的錯誤級別,當達到連接限制時,將會產生錯誤日志。

 

上面的配置示例中,沒有顯式配置 limit_conn_status 、limit_conn_log_level ,如果沒有配置,則啟用它們的默認值。

 

 

2.3 測試

接下來,對 limit_conn_zone 模塊的功能進行測試。

 

(1)測試1:對頁面訪問的並發限制

首先,來嘗試用戶對頁面訪問的並發限制,配置如下:

 

 

通過配置,設定 location /download 這個區域,每個IP,同一時刻只存在一個連接。注意:並發的概念並不是說 每秒多少連接。

生成一個較大的 index.html 頁面:

[root@10.0.10.158 /usr/local/nginx/conf]#cd ../html/
[root@10.0.10.158 /usr/local/nginx/html]#mkdir download/
[root@10.0.10.158 /usr/local/nginx/html]#cd download/
[root@10.0.10.158 /usr/local/nginx/html/download]#for i in {1..10000}; do echo "hello, $i" >> index.html ; done
[root@10.0.10.158 /usr/local/nginx/html/download]#ll -sh index.html
192K -rw-r--r-- 1 root root 117K Mar  8 21:16 index.htm

 

 

客戶端主機:10.0.10.159 通過 ab 命令模擬並發訪問:

[root@10.0.10.159 ~]#ab -n 10 -c 10 http://10.0.10.158/download/index.html

 

-n:總請求數:10

-c:單個時刻並發 10

 

 

通過上面的測試,只有 2 個連接錯誤。經過多次測試,這個問題依然存在,也查閱過一些資料,有人表示這是個 BUG 的。目前還是表示疑惑的,希望有人能夠幫忙解惑。

 

(2)測試2:對文件下載進行測試,或者是訪問較大的文件

生成一個 100M  的文件,因為局域網 千兆網絡,所以生成的比較大。

[root@10.0.10.158 /usr/local/nginx/html/download]#dd if=/dev/zero of=testfile bs=1M count=100

 

 然后在用 ab 進行測試:

[root@10.0.10.159 ~]#ab -n 10 -c 10 http://10.0.10.158/download/testfile

 

 

經過這次的測試,完成了 10 個並發連接,其中 9 個返回的是 非200 的狀態。繼續查看 Nginx 日志:

 

10 個並發連接,其中只有 1 個返回 200 狀態,其余 9 個都是 503 狀態,這樣的測試結果和設定的配置相符。

 

2.4 總結

通過上面兩次測試結果來看:第一次測試結果,配置的並發請求為 1 的限制貌似沒有起到作用。第二次測試結果,完全符合配置項的限制規則。而這兩次測試唯一的不同就是:數據文件大小不同

這里只能猜想:對於較小的文件或頁面,在千兆網絡的環境里,單個時刻請求數據的速度很快,並沒有造成多個連接同時並發的情況產生,而對於較大的頁面或文件來說,文件正在傳輸的時候處於 ESTABLISHED連接時間長,非常容易形成並發的效果,並發就會觸發限制規則。這也只是簡單的猜想,希望有大神能夠給出具體的科學的解釋。

 

 

3. limit_request 模塊

  使用 ngx_http_limit_req_module 模塊可以 限制某一 IP 在一段時間內對服務器發起請求的連接數,該模塊為內置模塊。

 

3.1 配置示例

 

3.2 指令

指令名稱:limit_req_zone

語法:limit_req_zone key zone=name:size rate= number r/s

默認值:no

區域:http

使用示例:limit_req_zone $binary_remote_addr zone=addr:10m rate=1r/s

對於上面的示例:

$binary_remote_addr :表示通過remote_addr 這個標識來做限制.

zone
=addr:10m:表示生成一個 10M ,名字為 addr 的內存區域,用來存儲訪問的頻次信息

rate
=1r/s:表示允許相同標識的客戶端的訪問頻次,這里限制的是每秒1次,即每秒只處理一個請求,還可以有比如 30r/m , 即限制每 2秒 訪問一次,即每 2秒 才處理一個請求。

 

指令名稱:limit_req

語法:limit_req zone=name [burst=number] [nodelay | delay=number];

默認:no

區域:http、server、location

使用示例:limit_req zone=zone burst=5 nodelay;

zone=zone:設置使用哪個配置名來做限制,與上面 limit_req_zone 里的 name 對應

burst=5 :這個配置的意思是設置一個大小為5的緩沖區,當有大量請求過來時,超過訪問頻次限制 rate=1r/s 的請求可以先放到這個緩沖區內等待,但是這個緩沖區只有5個位置,超過這個緩沖區的請求直接報503並返回。

nodelay:如果設置,會在瞬間提供處理(rate+burst)個請求的能力,請求超時(rat+burst)的時候直接返回503,永遠不存在請求需要等待的情況。如果沒有設置,則所有請求會依次等待排隊;

 

指令名稱:limit_req_status

語法:limit_req_status code;

默認:limit_req_status 503;

區域:http、server、location

功能:設置要返回的狀態碼以響應被拒絕的請求。

 

指令名稱:limit_req_log_level

(該指令出現在版本0.8.18中)

語法:limit_req_log_level info | notice | warn | error;

默認:limit_req_log_level error;

區域:http、server、location

功能:該指令用於設置日志的錯誤級別,當達到連接限制時,將會產生錯誤日志。

 

上面的示例內容沒有顯式寫明 limit_req_status 、limit_req_log_level 兩個配置項,所以采用默認值。

 

這里有幾個不太好理解地方:

limit_req_zone 后面的 rate=1r/s

limit_req  burst=5

 rate 為規定時間內連接請求的數量,單位(request/second)

burst 為爆咋的意思,這里可以理解為連接等待隊列長度。

 

這里就使用到了 ‘漏斗算法(Leaky Bucket)’,該算法有兩種處理方式 Traffic Shaping 和 Traffic Policing

在桶滿了之后,常用的兩種處理方式為:

  1. 暫時攔截住上方水的向下流動,等待桶中的一部分水漏走后,再放行上方水;

  2. 溢出的上方水直接拋棄。

 

舉個栗子:

比如上面的配置: rate=1r/s  burst=5
第一秒: 來了6個請求 - 1 個處理 - 5個等待
第二秒:來了2個請求 - 1個處理 - 5個等待 - 1 個丟棄

上面總共8個請求,時間2秒,2秒處理了2個請求,還剩6個,但是緩沖隊列只能存放5個請求,剩下1個丟棄。

或者

第一秒:來了10個請求 - 1個處理 - 5個等待 - 4個丟棄

 

就是按照這樣的方式來進行計算的,也就是說,上面的配置,每秒最多只能保持 6 個請求,但是每秒最多只能處理 1 個請求。

 

3.3 測試

 本模塊測試分為三種不同的方式進行測試驗證:

  (1)不加 burst 和 不加 nodelay

  (2)加 burst 和 不加 nodelay

  (3)加burtst 和 加 nodelay

 

測試1 - 不加 burst 和 不加 nodelay

配置如下:

 

客戶端主機:10.0.10.159 通過 ab 命令模擬並發訪問:

[root@10.0.10.159 ~]#ab -n 10 -c 10 http://10.0.10.158/index.html

 

 

通過 ab 測試后的結果,10 個並發連接,只有 1 個成功,剩余 9 個都返回 非200 狀態,查看 Nginx 日志:

 

1秒鍾的10次並發請求,只有 1 個返回 200 ,剩余全是 503 請求被拒絕。

不加 burst 和 不加 nodelay 的情況下,rate=1r/s  1 秒鍾只能處理 1 個請求,剩余的所有請求都會直接返回 503

 

測試2 - 加 burst 和 不加 nodelay

配置如下:

 

 客戶端主機:10.0.10.159 通過 ab 命令模擬並發訪問:

[root@10.0.10.159 ~]#ab -n 10 -c 10 http://10.0.10.158/index.html

 

 

處理了10個連接請求,其中失敗了 4 個,成功了 6個。

在本次測試中,使用 burst = 5 建立了一個可以存放 5 個並發連接的緩沖區。根據上面的 漏斗算法 來進行分析:

第一秒:10個連接並發請求,1個處理 5個等待,4個丟棄;

第二秒:1個處理 4 個等待;

第三秒:1個處理 3 個等待;

第四秒:1個處理 2 個等待;

第五秒:1個處理 1 個等待;

第六秒:1個處理 完畢。

 

通過上面的推算,一共需要 6 秒才能處理完所有的請求,查看 Nginx 日志,驗證推算是不是正確:

 

通過日志,可以看出,第一秒處理了 1 個請求,拒絕了 4 個連接,剩下的請求分別是每秒 1個連接請求的處理。

規則:rate=1r/s 、burst=5 

一次來了 10 個連接並發請求,處理 1 個,緩存 5 個后續 1 秒一個的處理,其他的全部丟棄。

 

加 burst 和 不加 nodelay 的情況下,rate=1r/s  burst=5 處理 1 個,緩存 5 個后續 1 秒一個的處理,其他的全部丟棄。

 

測試3 - 加 burst 和 加 nodelay

配置如下:

 

 客戶端主機:10.0.10.159 通過 ab 命令模擬並發訪問:

[root@10.0.10.159 ~]#ab -n 10 -c 10 http://10.0.10.158/index.html

 

 

10 個並發連接請求,很快就直接返回了,其中 4 個非200錯誤, 6 個成功。查看 Nginx 日志:

 

這次的測試結果,同一秒鍾,處理了 rate+burst 個請求,其他的全部返回連接請求拒絕。

 

3.4 總結

通過測試的三種情況,返回了不同的結果,因此有必要詳細說明:

(1)不加 burst 和 不加 nodelay 的情況:按照 rate 設定的規則,嚴格執行。例如:rate=1r/s ,則1秒只處理1個請求,其他的全部返回連接503

(2)加 burst 和 不加 nodelay 的情況:首先按照 rate 規則處理,並且緩存 burst 個連接,剩余的全部返回503,后續緩存的 burst 按照 rate 規則進行處理

(3)加 burst 和 nodelay 的情況:第一次處理 rate+burst 個連接請求,剩余的請求全部返回 503

 

4. limit_rate 根據 ip 限制流量

  對於提供下載的網站,肯定是要進行流量控制的。Nginx 通過 core模塊的 limit_rate 等指令可以做到限流的目的。

 

4.1 示例

 

 

4.2 指令

 

指令名稱:limit_rate

語法:limit_rate speed;

默認值:no

使用環境:http、server、location

示例: limit_rate 512k;

功能:該指令用於指定向客戶端傳輸數據的速度,速度的單位是每秒傳輸的字節數。注意:該限制只是針對一個連接的設定,也就是說,如果同時有2個連接,那么它的速度將會是該指令設置的兩倍。

 

指令名稱:limit_rate_after

語法:limit_rate_after size;

默認值:limit_rate_after 1m;

使用環境:http、server、location

示例:limit_rate_after 3m;

功能:以最大的速度下載 size大小后,在進行 limit_rate speed 限速,例如:limit_rate_after 3m 解釋為:以最大的速度下載3m后,再進行限速。

 

 

4.3 測試

  測試前疑問:對於這個模塊是通過什么來進行限速的呢? 上面的兩個模塊都有聲明 $binary_remote_addr 遠端ip地址進行操作的,而 limit_rate 什么都沒規定。針對這個問題,做以下測試:

配置如下:

正常速度下載 3m數據后,限速 512k 的速度下載。

 

生成一個較大的下載文件:

[root@10.0.10.158 /usr/local/nginx/html]#dd if=/dev/zero of=testfile bs=1M count=50

 

這次通過,curl 來下載文件:

這里使用的是千兆網絡,超過3m,速度被限制在512K 以下。這是一個連接下載,如果同時開啟多個終端,都進行下載呢?

連接-1

連接-2

 

因為 limit_rate 並沒有聲明以什么條件作為限制,所以同一個ip無論發起多少個請求,每個請求都會是 512k 下載。在迅雷等多種下載軟件中,使用多線程的方式下載同一個文件,這個速度就翻倍了。 那這個限速就形同虛設。

因此,如果要進行限速,可以和 limit_conn_zone 模塊配合進行使用。配置如下:

 

同一時刻,只有一個連接請求。

再進行同主機多線程下載:

 

連接-1

 

連接-2

 

當再次發起第二個連接的時候,服務器就直接返回連接拒絕 503,這樣就達到了限速的目的。

 

4.4 總結

  當需要進行限速操作時,需要 limit_rate  和 limit_conn 模塊聯合起來使用才能達到限速的效果。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM