Nginx 限速使用的是漏桶算法,此算法圖示如下,一個桶有一定的容量,水從桶的上方流入,如果桶中有水,水就會從下方按照一定的速率流出。
當然如果桶的容量已滿,流入的部分水就會溢出。如果桶沒有滿,水流入速度大于流出速度,那么桶的容量就會上升。
類比nginx環(huán)境,設(shè)置限速是1秒100個請求。Nginx時間粒度是毫秒,也就是10ms允許通過1個請求。那么可以認為桶的容量(10ms)是1。如果10ms到達2個請求,那么1個請求可以通過,1個會被拒絕。如果10毫秒到達1個請求,那么都可以順利的通過。這是非常簡單的場景,實際上漏桶算法也會考慮burst突發(fā)的場景。
漏桶算法桶的容量需要包括rate和burst部分。rate表示流出速率,burst表示突發(fā)情況下,允許的最大隊列。
漏桶算法算法常被用作(Traffic Shaping)或速率限制(Rate Limiting)。
如何配置最基本的限速?
配置Nginx 配置限速主要需要兩個指令limit_req_zone 用來配置限速,limit_req用來使用限速
limit_req_zone一般被配置在http作用域,需要三個參數(shù):
Key 用來設(shè)置一次請求的key,比如上圖,設(shè)置的是用戶二進制IP。
Zone 設(shè)置一個共享的區(qū)域。用來保存每個特定的IP狀態(tài),以及此IP訪問被限速的url頻次等。Zone等號后面是共享區(qū)域的名字。名字后面的冒號表示共享區(qū)域的大小。1M空間可以保存16000個IP信息,本例子可以大約保存1.6W個IP信息。
如果此空間被耗盡,nginx嘗試去移除老的記錄,但是如果空間仍然不夠用的話,NGINX會返回503 (Service Temporarily Unavailable)。
Rate 設(shè)置允許的最大請求速率。再次強調(diào)Nginx時間粒度是毫秒,對于本例子也就是100毫秒,只允許通過一個請求。
Nginx如何處理突發(fā)?
如果我們在100毫秒內(nèi)收到兩個請求呢?對于第二個請求,NGINX將狀態(tài)碼503返回給客戶端。這可能不是我們想要的,因為應(yīng)用程序在本質(zhì)上往往是突發(fā)的。,我們更希望能緩沖多余的請求,盡可能及時處理。NGINX可以通過limit_req指令的burst參數(shù)覆蓋這種場景:
burst參數(shù)設(shè)置,如果請求超過限速設(shè)置后,系統(tǒng)仍然可以接受多少請求,而不是直接拒絕。如上圖,桶的容量相當于21.如果100ms之內(nèi)一個特定的IP地址,到來21個請求。Nginx會立即將一個請求送到upstream處理。剩下的20個請求就會入burst隊列,此隊列按照先進先出的方式消費。緊接著100ms,nginx直接從隊列的取出一個請求,進行消費。如果請求超過了burst隊列容量,請求依然會被拒絕。
Nginx 處理觸發(fā) NoDelay。
limit_req配置burst參數(shù)后,一定程度上支持突發(fā)的流量。但是根據(jù)上面的介紹大家也看到了,其實消費速度還是100ms一個,只是先進了一個burst隊列。
另一種場景,假設(shè)被入隊的20個請求,第20個被處理的時候已經(jīng)是2s之后了。而這20個請求是有關(guān)聯(lián)的。那么第20個請求返回給用戶的時候,可能用戶已經(jīng)不在需要了。
針對這種場景nginx提供了一個額外參數(shù)nodelay
使用nodelay參數(shù),當一個burst請求到達時,只要隊列中有slot,nginx會立即轉(zhuǎn)發(fā),但是它將這個slot標記為“已占用”,直到適當?shù)臅r間過去(在我們的示例中,在100毫秒之后)才釋放它供另一個請求使用。
假設(shè)隊列20 slot是空的,并且有21個請求同時從給定的IP地址到達。NGINX立即轉(zhuǎn)發(fā)所有21個請求,并標記隊列中的20個slot,然后每100毫秒釋放一個slot。(如果有25個請求,NGINX會立即轉(zhuǎn)發(fā)其中21個,標記20個slot,拒絕剩下的4個請求)。
現(xiàn)在假設(shè)新的100ms內(nèi),又有20個請求同時到達。但是隊列中只有一個slot被釋放,因此NGINX轉(zhuǎn)發(fā)一個請求,并拒絕其他19個請求。
我們可以看到加了nodelay參數(shù)后,在突發(fā)的情況下QPS會短暫超過設(shè)置的rate。但是從平均水平看,依舊是1秒10個。
注意:對于大多數(shù)部署,nginx建議在limit_req指令中包含burst和nodelay參數(shù)。
如何改變限速返回值?
默認情況下,當客戶端超過其速率限制時,NGINX返回503(Service Temporarily Unavailable)。使用limit_req_status指令設(shè)置不同的狀態(tài)碼(本例中為444):
如何調(diào)整超限日志級別?
超限后的日志默認是error,用戶可以通過limit_req_log_level
,指令調(diào)整日志級別。
參考
https://blog.csdn.net/tjcyjd/article/details/77916146
https://www.nginx.com/blog/rate-limiting-nginx/
https://www.cnblogs.com/SUNSHINEC/p/9577682.html