NGINX 的速率限制(限流)
NGINX最有用但經常被誤解和配置錯誤的功能之一是限流。它允許您限制用戶在給定時間段內可以發出的HTTP請求量。
限流可以用於安全目的,例如減慢暴力破解密碼的攻擊。它可以通過限制請求速率爲真實用戶的典型值來幫助防禦分佈式拒絕服務(DDoS)攻擊,並且(通過記錄日誌)可以識別被攻擊的URL。更一般地說,它用於保護上游應用服務器免受過多用戶請求同時到達而導致的壓力過大。
在本博客中,我們將介紹 NGINX 限流的基礎知識以及更高級的配置。當然限流在 NGINX Plus 中的工作方式相同。
NGINX限流的工作原理
NGINX 限流使用漏桶算法,該算法廣泛用於電信和數據包交換計算機網絡,用於在帶寬有限時處理突發性。好比一個水桶,水從上面倒入,從下面漏出;如果倒入水的速度超過漏水的速度,水桶就會溢出。在請求處理方面,水代表客戶端的請求,桶代表一個隊列,請求按照先進先出(FIFO)調度算法等待處理。漏水代表退出緩衝區供服務器處理的請求,溢出代表被丟棄且從未得到服務的請求。
限流基本配置
限流由兩個主要指令limit_req_zone和進行配置limit_req,如下例所示:
需要注意的一個點是rate速率是正整數,/s是每秒;/m是每分
limit_req_zone $binary_remote_addr zone=mylimit:10m rate=10r/s; server { location /qhyu/ { limit_req zone=mylimit; proxy_pass http://my_upstream; } }
該limit_req_zone指令定義了速率限制的參數,同時limit_req在它出現的上下文中啓用速率限制(在示例中,對於 /qhyu/ 的所有請求)。
我用postman做了一個併發測試,除了第一個接口返回200,其他的都被限流了。
nginx日誌如下:
該limit_req_zone指令通常在http塊中定義,使其可在多個上下文中使用。它需要以下三個參數:
key-定義應用的請求特徵。在示例中,它是nginx變量 $binary_remote_addr,它保存客戶端IP地址的二進制表現形式。這意味着我們將每個唯一的IP地址限制爲第三個參數定義的請求速率。(我們使用此變量是因爲它比客戶端 IP 地址的字符串表示佔用的空間更少$remote_addr)
zone-定義用於存儲每個IP地址的狀態及其訪問請求限制URL的頻率的共享內存區域。將信息保存在共享內存中意味着它可以在nginx工作進程之間共享。該定義有兩部分:由關鍵字標識的區域名稱zone=,以及冒號後面的大小。大約16000個IP地址的狀態信息需要1兆字節,因爲我們的區域可以存儲大約160000個地址。
如果nginx需要添加新條目時存儲空間耗盡,它將刪除最舊的條目。如果釋放的空間仍不足以容納新記錄,nginx將返回狀態碼。此外,爲了防止內存耗盡,每次nginx創建條目時,它都會刪除最多兩個在前60s內未使用的條目。503 Service Temporarily Unavailable
rate-設置最大請求速率。在示例中,速率不能超過每秒10個請求。nginx實際上以毫秒粒度跟蹤請求,因此此限制對應於每 100 毫秒 (ms) 1 個請求。因爲我們不允許突發(請參閱下一節),這意味着如果請求在上一個允許的請求之後不到 100 毫秒到達,則該請求將被拒絕。
該limit_req_zone指令設置了速率限制和共享內存區域的參數,但它實際上並不限制請求速率。爲此,您需要通過在其中包含指令來將限制應用於特定location或塊。在示例中,我們對 /qhyu/ 的請求進行速率限制。limit_req
因此,現在每個唯一的 IP 地址限制爲每秒 10 次 /qhyu/ 請求 ,或者更準確地說,不能在前一個 URL 的 100 毫秒內對該 URL 發出請求。
處理突發
如果我們在 100 毫秒內收到 2 個請求怎麼辦?對於第二個請求,NGINX 將狀態代碼返回503給客戶端。一些業務場景下這可能不是我們想要的,因爲應用程序本質上往往是突發性的。相反,我們希望緩衝任何多餘的請求並及時爲它們提供服務。如這個更新的配置所示:
location /qhyu/ { limit_req zone=mylimit burst=20; proxy_pass http://my_upstream; }
該burst參數定義客戶端可以發出超出區域指定速率的請求數量(對於我們的示例mylimit區域,速率限制爲每秒 10 個請求,或每 100 毫秒 1 個請求)。比上一個請求晚 100 毫秒到達的請求將被放入隊列中,這裏我們將隊列大小設置爲 20。
這意味着如果 21 個請求同時從給定的 IP 地址到達,NGINX 會立即將第一個請求轉發到上游服務器組,並將剩餘的 20 個放入隊列中。然後,它每 100 毫秒轉發一個排隊請求,503只有當傳入請求使排隊請求數量超過 20 時才返回客戶端。
排隊無延遲
這種配置burst可以使流量流暢,但不太實用,因爲它會使您的網站顯得很慢。在我們的示例中,隊列中的第 20 個數據包等待 2 秒才能轉發,此時對其的響應可能不再對客戶端有用。爲了解決這種情況,將nodelay參數與參數burst一起添加:
location /qhyu/ { limit_req zone=mylimit burst=20 nodelay; proxy_pass http://my_upstream; }
使用該nodelay參數,NGINX 仍然根據該burst參數在隊列中分配插槽並施加配置的速率限制,但不會間隔轉發排隊的請求。相反,當請求“太快”到達時,NGINX 會立即轉發它,只要隊列中有可用的槽位。它將該插槽標記爲“已佔用”,並且在經過適當的時間(在我們的示例中,100 毫秒後)之前不會將其釋放以供其他請求使用。
與之前一樣,假設 20 槽隊列爲空,並且有 21 個請求同時從給定 IP 地址到達。NGINX 立即轉發所有 21 個請求,並將隊列中的 20 個槽標記爲已佔用,然後每 100 毫秒釋放 1 個槽。(如果有 25 個請求,NGINX 將立即轉發其中 21 個,將 20 個槽標記爲已佔用,並拒絕 4 個狀態爲503的請求。)
現在假設第一組請求轉發後 101 毫秒,另外 20 個請求同時到達。隊列中只有 1 個槽被釋放,因此 NGINX 轉發 1 個請求並拒絕其他 19 個狀態爲 的請求 503。相反,如果在 20 個新請求到達之前已經過去了 501 毫秒,則有 5 個插槽空閒,因此 NGINX 會立即轉發 5 個請求並拒絕 15 個。
效果相當於每秒 10 個請求的速率限制。nodelay如果您想要施加速率限制而不限制請求之間允許的間距,則該選項非常有用。
注意: 對於大多數部署,我們建議 在指令limit_req中包含burst和nodelay 參數。
兩階段限速
綜合上述內容,我們可以將nginx配置爲允許突發請求以適應典型的web瀏覽器請求模式,然後將額外的過多請求限制到某個點,通過閾值拒絕其他過多的請求。使用limit_req指令的delay參數啓用兩級速率限制。
爲了說明兩階段速率限制,這裏我們配置 NGINX 通過施加每秒 5 個請求 (r/s) 的速率限制來保護網站。該網站通常每頁有 4-6 個資源,但永遠不會超過 12 個資源。該配置允許突發最多 12 個請求,其中前 8 個請求將立即得到處理。在 8 個過多請求後添加延遲以強制執行 5 r/s 限制。超過 12 次請求後,任何進一步的請求都會被拒絕。
limit_req_zone $binary_remote_addr zone=ip:10m rate=5r/s; server { listen 80; location / { limit_req zone=ip burst=12 delay=8; proxy_pass http://website; } }
該delay參數定義了在突發大小內限制(延遲)過多請求以符合定義的速率限制的點。完成此配置後,以 8 r/s 發出連續請求流的客戶端會經歷以下行爲。
前 8 個請求( delay的值)由 NGINX無延遲地代理。接下來的 4 個請求 ( burst - delay) 將被延遲,以便不超過定義的 5 r/s 速率。接下來的 3 個請求將被拒絕,因爲已超出總突發大小。後續請求將被延遲。
高級配置示例
通過將基本速率限制與其他 NGINX 功能相結合,您可以實現更細緻的流量限制。
列入白名單
此示例演示如何對不在“允許列表”中的任何人的請求施加速率限制。
geo $limit { default 1; 10.0.0.0/8 0; 192.168.0.0/24 0; } map $limit $limit_key { 0 ""; 1 $binary_remote_addr; } limit_req_zone $limit_key zone=req_zone:10m rate=5r/s; server { location / { limit_req zone=req_zone burst=10 nodelay; # ... } }
此示例同時使用了geo和map指令
如果$limit是0則$limit_key設置爲空字符串
如果$limit是1,$limit_key則設置爲二進制格式的客戶端 IP 地址
將兩者放在一起,$limit_key對於列入白名單的 IP 地址,設置爲空字符串,否則設置爲客戶端的 IP 地址。當目錄的第一個參數limit_req_zone(密鑰)爲空字符串時,不應用限制,因此列入白名單的 IP 地址(在 10.0.0.0/8 和 192.168.0.0/24 子網中)不受限制。所有其他 IP 地址限制爲每秒 5 個請求。
該limit_req指令將限制應用於 / 位置,並允許突發超過配置限制的最多 10 個數據包,且轉發無延遲
在一個位置中包含多個limit_req指令
可以limit_req在一個位置包含多個指令。應用與給定請求匹配的所有限制,這意味着使用最嚴格的限制。例如,如果多個指令施加延遲,則使用最長的延遲。同樣,如果這是任何指令的效果,則請求將被拒絕,即使其他指令允許它們通過。
擴展前面的示例,我們可以對白名單上的 IP 地址應用速率限制:
http { # ... limit_req_zone $limit_key zone=req_zone:10m rate=5r/s; limit_req_zone $binary_remote_addr zone=req_zone_wl:10m rate=15r/s; server { # ... location / { limit_req zone=req_zone burst=10 nodelay; limit_req zone=req_zone_wl burst=20 nodelay; # ... } } }
允許名單上的 IP 地址與第一個速率限制 ( req_zone )不匹配,但與第二個速率限制 ( req_zone_wl )匹配,因此限制爲每秒 15 個請求。不在允許列表中的 IP 地址與兩個速率限制均匹配,因此選擇限制性更強的速率限制:每秒 5 個請求。
配置相關功能
記錄
日誌條目中的字段包括:
2023/08/25 04:20:00 – 寫入日誌條目的日期和時間
[error] – 嚴重程度
120315#0 – NGINX工作線程的進程ID和線程ID,用符號#分隔
*32086 – 速率受限的代理連接的 ID
limiting requests – 指示日誌條目記錄速率限制
excess – 此請求所代表的配置速率上的每毫秒請求數
zone – 定義施加速率限制的區域
client – 發出請求的客戶端的 IP 地址
server – 服務器的 IP 地址或主機名
request – 客戶端發出的實際 HTTP 請求
host – HostHTTP 標頭的值
默認情況下,NGINX 在該級別記錄拒絕的請求,如上例error所示。[error](默認情況下,它會在低一級記錄延遲請求warn。)要更改日誌記錄級別,請使用該limit_req_log_level指令。這裏我們將拒絕的請求設置爲日誌級別warn:
location /qhyu/ { limit_req zone=mylimit burst=20 nodelay; limit_req_log_level warn; proxy_pass http://my_upstream; }
發送給客戶端的錯誤代碼
默認情況下,當客戶端超出其速率限制時,NGINX 會返回狀態代碼503 ( Service Temporarily Unavailable) 。使用limit_req_status指令設置不同的狀態代碼(444在本例中):
location /login/ { limit_req zone=mylimit burst=20 nodelay; limit_req_status 444; }
拒絕對特定位置的所有請求
如果您想拒絕對特定 URL 的所有請求,而不僅僅是限制它們,請location爲其配置一個塊幷包含以下指令:
location /foo.php {
deny all;
}
NGINX的速率限制(限流)_nginx 限速-CSDN博客
https://blog.csdn.net/Tanganling/article/details/132491643
Nginx額外篇之實現Nginx限流(運維篇)-CSDN博客
https://blog.csdn.net/yts1115084429/article/details/102494745
限制併發連接數
參考
搞定Nginx限流,這一篇就夠了
https://baijiahao.baidu.com/s?id=1634400719648980722&wfr=spider&for=pc