限流(Rate Limitting)是服務(wù)降級(jí)的一種方式艳悔,通過(guò)限制系統(tǒng)的輸入和輸出流量以達(dá)到保護(hù)系統(tǒng)的目的。比如我們的網(wǎng)站暴露在公網(wǎng)環(huán)境中香府,除了用戶的正常訪問(wèn)企孩,網(wǎng)絡(luò)爬蟲(chóng)袁稽、惡意攻擊或者大促等突發(fā)流量都可能都會(huì)對(duì)系統(tǒng)造成壓力勿璃,如果這種壓力超出了服務(wù)器的處理能力补疑,會(huì)造成響應(yīng)過(guò)慢甚至系統(tǒng)崩潰的問(wèn)題莲组。因此锹杈,當(dāng)并發(fā)請(qǐng)求數(shù)過(guò)大時(shí),我們通過(guò)限制一部分請(qǐng)求(比如限制同一IP的頻繁請(qǐng)求)來(lái)保證服務(wù)器可以正確響應(yīng)另一部分的請(qǐng)求竭望。
Nginx的limit模塊主要包括: ngx_http_limit_req_module咬清、ngx_http_limit_conn_module旧烧、ngx_stream_limit_conn_module以及ngx_http_core_module中l(wèi)imit_rate選項(xiàng),由于stream主要用來(lái)實(shí)現(xiàn)四層協(xié)議(網(wǎng)絡(luò)層和傳輸層)的轉(zhuǎn)發(fā)斧拍、代理杖小、負(fù)載均衡等予权,并且ngx_stream_limit_conn_module和ngx_http_limit_conn_module配置基本相同
限制請(qǐng)求速率ngx_http_limit_req_module
req_module提供限制請(qǐng)求處理速率的能力扫腺,使用了漏桶算法村象。我們可以想像有一只上面進(jìn)水、下面勻速出水的桶躁劣,如果桶里面有水库菲,那剛進(jìn)去的水就要存在桶里等下面的水流完之后才會(huì)流出熙宇,如果進(jìn)水的速度大于水流出的速度,桶里的水就會(huì)滿蒋荚,這時(shí)水就不會(huì)進(jìn)到桶里馆蠕,而是直接從桶的上面溢出。對(duì)應(yīng)到處理網(wǎng)絡(luò)請(qǐng)求吓妆,水代表從客戶端來(lái)的請(qǐng)求行拢,而桶代表一個(gè)隊(duì)列诞吱,請(qǐng)求在該隊(duì)列中依據(jù)先進(jìn)先出(FIFO)算法等待被處理。漏的水代表請(qǐng)求離開(kāi)緩沖區(qū)并被服務(wù)器處理沼瘫,溢出代表了請(qǐng)求被丟棄并且永不被服務(wù)。
正常限流
該模塊用來(lái)限制某個(gè)特定鍵的請(qǐng)求處理的速率湿故,這個(gè)特定鍵一般為某個(gè)ip坛猪。該模塊主要用到了"漏桶"算法墅茉。有關(guān)漏桶算法可以點(diǎn)擊此處詳細(xì)查看就斤。
limit_req_zone $binary_remote_addr zone=one:10m rate=1r/s;
server {
location /search/ {
limit_req zone=one burst=5 [nodelay|delay=num];
}
首先使用limit_req_zone定義一個(gè)內(nèi)存區(qū)域(定義在http段中):
$binary_remote_addr: 特定用于該模塊的變量洋机,用來(lái)存取客戶端ip地址以躯。
zone=one:10m:定義一個(gè)大小為10m的共享內(nèi)存區(qū)域,名稱(chēng)為one刁标。對(duì)于ipv4地址占用4個(gè)字節(jié)膀懈,ipv6占用16個(gè)字節(jié)启搂。存儲(chǔ)一個(gè)連接狀態(tài)在32位機(jī)器上占用64個(gè)字節(jié)刘陶,在64位機(jī)器上占用128個(gè)字節(jié)匙隔。1MB的空間可以存儲(chǔ)16k個(gè)64字節(jié)狀態(tài)或者是8k個(gè)128字節(jié)狀態(tài)。所以10m的空間可以存儲(chǔ)大約8w個(gè)狀態(tài)捍掺,對(duì)于一般的中小型站點(diǎn)已經(jīng)足夠了挺勿。
rate=1r/s:定義請(qǐng)求速率為每秒僅接受一個(gè)請(qǐng)求
在server配置段中引用上述配置(第5行):
zone=one:引用上面limit_req_zone定義的內(nèi)存區(qū)域one
burst:定義請(qǐng)求緩存的長(zhǎng)度,意思是如果某ip1秒內(nèi)發(fā)來(lái)了10個(gè)請(qǐng)求不瓶,那么除了正在處理的1個(gè)請(qǐng)求,其他的請(qǐng)求會(huì)把暫時(shí)放置到burst中排隊(duì)湃番。超過(guò)rate+burst請(qǐng)求將會(huì)被全部丟棄
nodelay:一次處理burst+rate個(gè)請(qǐng)求吠撮,其余全部丟棄泥兰。如果不希望在請(qǐng)求受到限制時(shí)延遲過(guò)多的請(qǐng)求應(yīng)當(dāng)使用這個(gè)參數(shù)
delay=num:瞬時(shí)處理num+rate個(gè)請(qǐng)求,總共緩存burst個(gè)题禀,剩余的burst-num按rate定義處理鞋诗。丟棄多余請(qǐng)求。
其他指令
- limit_req_dry_run
語(yǔ)法: limit_req_dry_run on | off
默認(rèn)值: limit_req_dry_run off;
語(yǔ)境: http server location
該指令開(kāi)啟"干跑"模式迈嘹。開(kāi)啟該配置削彬,請(qǐng)求處理的速率不再被限制。但是在共享內(nèi)存區(qū)域秀仲,過(guò)量的請(qǐng)求的數(shù)值會(huì)像之前一樣計(jì)算融痛。
- limit_req_log_level
語(yǔ)法: limit_req_log_level info | notice | warn | error;
默認(rèn)值: limit_req_log_level error;
語(yǔ)境: http server location
該指令設(shè)置rate超過(guò)限值或者延時(shí)請(qǐng)求處理的日志級(jí)別,默認(rèn)為error級(jí)別神僵。
- limit_req_status
語(yǔ)法: limit_req_status code;
默認(rèn)值: limit_req_status 503;
語(yǔ)境: http server location
該指令定義拒絕響應(yīng)請(qǐng)求的http狀態(tài)碼保礼,默認(rèn)返回*503
測(cè)試
1目派、不開(kāi)啟burst企蹭,不開(kāi)啟nodelay
配置如下所示:
http {
include mime.types;
default_type application/octet-stream;
limit_req_zone $binary_remote_addr zone=one:10m rate=1r/s;
log_format main '$remote_addr "$request"'
'$status'
'"$http_user_agent"';
server {
listen 80;
server_name localhost;
charset utf-8;
location / {
limit_req zone=one;
root /usr/share/nginx/html/;
index index.html index.htm;
}
}
}
在另一臺(tái)虛擬機(jī)使用Apahce Benchmark進(jìn)行壓力測(cè)試
ab -c 10 -n10 http://192.168.0.106/index.html
結(jié)果如下:
192.168.0.107 - - [05/May/2020:20:58:41 +0800] "GET / HTTP/1.0" 200 70 "-" "ApacheBench/2.3"
192.168.0.107 - - [05/May/2020:20:58:41 +0800] "GET / HTTP/1.0" 503 197 "-" "ApacheBench/2.3"
192.168.0.107 - - [05/May/2020:20:58:41 +0800] "GET / HTTP/1.0" 503 197 "-" "ApacheBench/2.3"
192.168.0.107 - - [05/May/2020:20:58:41 +0800] "GET / HTTP/1.0" 503 197 "-" "ApacheBench/2.3"
192.168.0.107 - - [05/May/2020:20:58:41 +0800] "GET / HTTP/1.0" 503 197 "-" "ApacheBench/2.3"
192.168.0.107 - - [05/May/2020:20:58:41 +0800] "GET / HTTP/1.0" 503 197 "-" "ApacheBench/2.3"
192.168.0.107 - - [05/May/2020:20:58:41 +0800] "GET / HTTP/1.0" 503 197 "-" "ApacheBench/2.3"
192.168.0.107 - - [05/May/2020:20:58:41 +0800] "GET / HTTP/1.0" 503 197 "-" "ApacheBench/2.3"
192.168.0.107 - - [05/May/2020:20:58:41 +0800] "GET / HTTP/1.0" 503 197 "-" "ApacheBench/2.3"
192.168.0.107 - - [05/May/2020:20:58:41 +0800] "GET / HTTP/1.0" 503 197 "-" "ApacheBench/2.3"
嚴(yán)格按照rate定義處理請(qǐng)求螟凭,除了第一個(gè)請(qǐng)求外其余所有的請(qǐng)求全部丟棄并返回503棒厘。
2奢人、在18行末尾添加burst=5
192.168.0.107 - - [05/May/2020:21:53:35 +0800] "GET /index.html HTTP/1.0" 200 70 "-" "ApacheBench/2.3"
192.168.0.107 - - [05/May/2020:21:53:35 +0800] "GET /index.html HTTP/1.0" 503 197 "-" "ApacheBench/2.3"
192.168.0.107 - - [05/May/2020:21:53:35 +0800] "GET /index.html HTTP/1.0" 503 197 "-" "ApacheBench/2.3"
192.168.0.107 - - [05/May/2020:21:53:35 +0800] "GET /index.html HTTP/1.0" 503 197 "-" "ApacheBench/2.3"
192.168.0.107 - - [05/May/2020:21:53:35 +0800] "GET /index.html HTTP/1.0" 503 197 "-" "ApacheBench/2.3"
192.168.0.107 - - [05/May/2020:21:53:36 +0800] "GET /index.html HTTP/1.0" 200 70 "-" "ApacheBench/2.3"
192.168.0.107 - - [05/May/2020:21:53:37 +0800] "GET /index.html HTTP/1.0" 200 70 "-" "ApacheBench/2.3"
192.168.0.107 - - [05/May/2020:21:53:38 +0800] "GET /index.html HTTP/1.0" 200 70 "-" "ApacheBench/2.3"
192.168.0.107 - - [05/May/2020:21:53:39 +0800] "GET /index.html HTTP/1.0" 200 70 "-" "ApacheBench/2.3"
192.168.0.107 - - [05/May/2020:21:53:40 +0800] "GET /index.html HTTP/1.0" 200 70 "-" "ApacheBench/2.3"
按照定義土辩,1秒內(nèi)來(lái)了10個(gè)請(qǐng)求各墨,burst=5贬堵,rate=1∫福總共緩存5個(gè)請(qǐng)求伟桅,處理1個(gè)請(qǐng)求,其余全部丟棄盖腕。這就是21:53:35內(nèi)丟棄了4個(gè)請(qǐng)求劲厌。隨后緩存的請(qǐng)求按照rate定義值處理补鼻。
3、在18行末尾繼續(xù)添加nodelay
192.168.0.107 - - [05/May/2020:21:58:19 +0800] "GET /index.html HTTP/1.0" 200 70 "-" "ApacheBench/2.3"
192.168.0.107 - - [05/May/2020:21:58:19 +0800] "GET /index.html HTTP/1.0" 200 70 "-" "ApacheBench/2.3"
192.168.0.107 - - [05/May/2020:21:58:19 +0800] "GET /index.html HTTP/1.0" 200 70 "-" "ApacheBench/2.3"
192.168.0.107 - - [05/May/2020:21:58:19 +0800] "GET /index.html HTTP/1.0" 200 70 "-" "ApacheBench/2.3"
192.168.0.107 - - [05/May/2020:21:58:19 +0800] "GET /index.html HTTP/1.0" 200 70 "-" "ApacheBench/2.3"
192.168.0.107 - - [05/May/2020:21:58:19 +0800] "GET /index.html HTTP/1.0" 200 70 "-" "ApacheBench/2.3"
192.168.0.107 - - [05/May/2020:21:58:19 +0800] "GET /index.html HTTP/1.0" 503 197 "-" "ApacheBench/2.3"
192.168.0.107 - - [05/May/2020:21:58:19 +0800] "GET /index.html HTTP/1.0" 503 197 "-" "ApacheBench/2.3"
192.168.0.107 - - [05/May/2020:21:58:19 +0800] "GET /index.html HTTP/1.0" 503 197 "-" "ApacheBench/2.3"
192.168.0.107 - - [05/May/2020:21:58:19 +0800] "GET /index.html HTTP/1.0" 503 197 "-" "ApacheBench/2.3"
瞬間提供rate+burst個(gè)請(qǐng)求處理能力,丟棄其他請(qǐng)求返回503寇漫。
4猪腕、將18行的nodelay改成delay=3
192.168.0.107 - - [05/May/2020:23:42:05 +0800] "GET / HTTP/1.0" 200 70 "-" "ApacheBench/2.3"
192.168.0.107 - - [05/May/2020:23:42:05 +0800] "GET / HTTP/1.0" 200 70 "-" "ApacheBench/2.3"
192.168.0.107 - - [05/May/2020:23:42:05 +0800] "GET / HTTP/1.0" 200 70 "-" "ApacheBench/2.3"
192.168.0.107 - - [05/May/2020:23:42:05 +0800] "GET / HTTP/1.0" 200 70 "-" "ApacheBench/2.3"
192.168.0.107 - - [05/May/2020:23:42:05 +0800] "GET / HTTP/1.0" 503 197 "-" "ApacheBench/2.3"
192.168.0.107 - - [05/May/2020:23:42:05 +0800] "GET / HTTP/1.0" 503 197 "-" "ApacheBench/2.3"
192.168.0.107 - - [05/May/2020:23:42:05 +0800] "GET / HTTP/1.0" 503 197 "-" "ApacheBench/2.3"
192.168.0.107 - - [05/May/2020:23:42:05 +0800] "GET / HTTP/1.0" 503 197 "-" "ApacheBench/2.3"
192.168.0.107 - - [05/May/2020:23:42:06 +0800] "GET / HTTP/1.0" 200 70 "-" "ApacheBench/2.3"
192.168.0.107 - - [05/May/2020:23:42:07 +0800] "GET / HTTP/1.0" 200 70 "-" "ApacheBench/2.3"
可以看出nginx瞬間處理了4(rate+delay)個(gè)請(qǐng)求彻采,緩存了2個(gè)請(qǐng)求,其余請(qǐng)求返回503特笋。緩存下來(lái)的請(qǐng)求每秒處理一個(gè)。
限制連接數(shù)量
conn_module與ngx_http_limit_req_module主要區(qū)別在于蔫磨,conn限制的連接的數(shù)量堤如,req限制的是請(qǐng)求的數(shù)量蝗岖。一個(gè)連接的生命周期中,會(huì)存在一個(gè)或者多個(gè)請(qǐng)求瓣俯,這是為了加快效率,避免每次請(qǐng)求都要三次握手建立連接驼仪,現(xiàn)在的HTTP/1.1協(xié)議都支持這種特性绪爸,叫做keepalive奠货。
官方配置示例:
http {
limit_conn_zone $binary_remote_addr zone=addr:10m;
server {
location /download/ {
limit_conn addr 1;
}
limit_conn_zone:設(shè)置共享區(qū)域的參數(shù),該區(qū)域?qū)⒈A舾鞣N鍵的狀態(tài)萍虽,與ngx_http_limit_req_module不同的是形真,此模塊僅定義了區(qū)域了大小杉编。鍵里可以保存文本,變量或者兩者的組合咆霜。
limit_conn:設(shè)置共享內(nèi)存區(qū)域以及鍵定義的最大允許的連接數(shù)量邓馒。超過(guò)限值時(shí),服務(wù)器將會(huì)發(fā)送錯(cuò)誤給客戶端裕便。limit_conn addr 1允許一個(gè)ip一次最多建立一次連接绒净。如果是HTTP/2,那么每個(gè)并發(fā)的請(qǐng)求都會(huì)被視作是一個(gè)單獨(dú)的連接偿衰。
limit_conn_dry_run,limit_conn_log_level,limit_conn_status與ngx_http_limit_req_module中一致不再說(shuō)明挂疆。
測(cè)試
192.168.0.107 - - [05/May/2020:23:54:27 +0800] "GET /test.html HTTP/1.0" 503 197 "-" "ApacheBench/2.3"
192.168.0.107 - - [05/May/2020:23:54:27 +0800] "GET /test.html HTTP/1.0" 503 197 "-" "ApacheBench/2.3"
192.168.0.107 - - [05/May/2020:23:54:27 +0800] "GET /test.html HTTP/1.0" 503 197 "-" "ApacheBench/2.3"
192.168.0.107 - - [05/May/2020:23:54:27 +0800] "GET /test.html HTTP/1.0" 503 197 "-" "ApacheBench/2.3"
192.168.0.107 - - [05/May/2020:23:54:27 +0800] "GET /test.html HTTP/1.0" 503 197 "-" "ApacheBench/2.3"
192.168.0.107 - - [05/May/2020:23:54:27 +0800] "GET /test.html HTTP/1.0" 503 197 "-" "ApacheBench/2.3"
192.168.0.107 - - [05/May/2020:23:54:27 +0800] "GET /test.html HTTP/1.0" 503 197 "-" "ApacheBench/2.3"
192.168.0.107 - - [05/May/2020:23:54:27 +0800] "GET /test.html HTTP/1.0" 503 197 "-" "ApacheBench/2.3"
192.168.0.107 - - [05/May/2020:23:54:27 +0800] "GET /test.html HTTP/1.0" 503 197 "-" "ApacheBench/2.3"
192.168.0.107 - - [05/May/2020:23:54:29 +0800] "GET /test.html HTTP/1.0" 200 536870912 "-" "ApacheBench/2.3"
test.html為50M改览,測(cè)試1秒內(nèi)僅能建立一個(gè)連接其余全部丟棄。但index.html測(cè)試失敗缤言,不知道和文件的大小存在什么關(guān)聯(lián)宝当?
limit_rate
limit_rate是ngx_http_core_module這個(gè)核心模塊自帶的一個(gè)配置選項(xiàng)庆揩「耄可以用來(lái)限制單個(gè)連接的下載速率。對(duì)于資源文件下載服務(wù)器來(lái)說(shuō)瑟幕,有必要限制這個(gè)值孵稽,防止單個(gè)ip過(guò)高的速率影響他人的正常使用许起。
語(yǔ)法: limit_rate rate;
默認(rèn)值: limit_rate 0;
語(yǔ)境: http, server, location, if in location
默認(rèn)情況下,這個(gè)值是0菩鲜,也就是不限制下載速率园细。官方推薦使用map來(lái)靈活定義下載速率。如:
map $slow $rate {
0 40k;
1 80k;
default 120k;
}
limit_rate $rate; #http接校,server猛频,location,if in location
當(dāng)然slow變量。
geo $remote_addr $slow {
default 0;
47.103.215.250 1;
}
以上是這樣的過(guò)程:remote_addr=47.103.215.250,則把rate,最后在limit_rate中應(yīng)用$rate來(lái)達(dá)到限制速率的要求皿淋。
注:geo 和 map都是應(yīng)用在http段中
本地下載測(cè)試為40KB/s
47.103.215.250上測(cè)試為80KB/s
當(dāng)然limit_rate只能限制單個(gè)ip的一個(gè)請(qǐng)求:
如果客戶端同時(shí)發(fā)起了兩個(gè)鏈接招刹,這個(gè)下載速度會(huì)變成限值的2倍。所以對(duì)于迅雷這種多線程的下載器窝趣,設(shè)置這樣的限值的無(wú)效的疯暑。
解決的方法就是配合ngx_http_limit_conn_module模塊,讓服務(wù)器每秒只響應(yīng)一個(gè)ip的一個(gè)請(qǐng)求哑舒,其余請(qǐng)求全部丟棄妇拯。
limit_conn_zone $binary_remote_addr zone=one:10m;
server {
listen 80;
server_name localhost;
location / {
limit_conn one 1;
limit_rate 80k;
root /usr/share/nginx/html/;
}
}
在測(cè)試端下載test.html
[root@k8s-node ~]# wget http://192.168.0.106/test.html
--2020-05-06 00:00:43-- http://192.168.0.106/test.html
Connecting to 192.168.0.106:80... connected.
HTTP request sent, awaiting response... 200 OK
Length: 536870912 (512M) [text/html]
Saving to: ‘test.html.2’
test.html.2
0%[ ] 1.29M 79.6KB/s eta 1h 48m
復(fù)制終端會(huì)話再次請(qǐng)求:
Last login: Tue May 5 23:40:24 2020 from 192.168.0.101
[root@k8s-node ~]# wget http://192.168.0.106/test.html
--2020-05-06 00:01:18-- http://192.168.0.106/test.html
Connecting to 192.168.0.106:80... connected.
HTTP request sent, awaiting response... 503 Service Temporarily Unavailable
2020-05-06 00:01:19 ERROR 503: Service Temporarily Unavailable.
nginx直接返回了503,這樣就達(dá)到了限制單個(gè)ip的請(qǐng)求速率的目的洗鸵。
參考:
https://blog.csdn.net/qq_35760825/article/details/127596936
https://www.cnblogs.com/ltzhang/p/13544562.html