Nginx的請求限制
在配置nginx的過程中我們需要考慮受到攻擊或惡意請求的情況,比如單用戶惡意發(fā)起大量請求落君,這時(shí)Nginx的請求限制可以幫助我們對(duì)其進(jìn)行限制穿香。
連接頻率限制 : limit_conn_module
請求頻率限制 : limit_req_module
理解:連接頻率限制和請求頻率限制都可以實(shí)現(xiàn)Nginx的請求限制 , 但是他們的實(shí)現(xiàn)原理是不一樣的 , 區(qū)別就在于連接和請求上 , http協(xié)議的鏈接與請求 , http協(xié)議是建立在tcp協(xié)議之上的,要完成一次http的請求,先要進(jìn)行tcp的3次握手建立http的連接 , 然后才進(jìn)行http的request和response(請求和響應(yīng)) , 現(xiàn)在http1.1以上的版本已經(jīng)可以實(shí)現(xiàn)一次建立http的連接進(jìn)行多次的http的request和response(請求和響應(yīng)) ,最后客戶端和服務(wù)端不斷的來發(fā)送FIN包和ACK包來保持HTTP的連接 。
如果面對(duì)搶購和秒殺需求來限制 , 個(gè)人覺得連接頻率限制和請求頻率限制應(yīng)該配合使用 , 使用連接頻率限制同一IP同時(shí)只能有3個(gè)連接, 再使用請求頻率限制對(duì)于同一ip的請求绎速,限制平均速率為5個(gè)請求/秒 , 這樣是不是比單獨(dú)只使用一種限制要好很多?
比如只使用連接頻率限制 , 由于一次建立http的連接可以進(jìn)行多次的請求和響應(yīng) , 我們無法精確的限制同一ip同時(shí)發(fā)起多少次的http請求 ;
比如只使用請求頻率限制 , 可以精確的限制同一ip1秒只能發(fā)起5次的http請求 , 假如同一ip1秒內(nèi)發(fā)起了100000次請求 , 雖然限制了只有5次成功響應(yīng) , 但是其他的99995次的請求TCP握手建立http連接是不是會(huì)消耗服務(wù)器資源?
所以,個(gè)人覺得連接頻率限制和請求頻率限制應(yīng)該配合使用!
HTTP協(xié)議的連接與請求
HTTP請求建立在一次TCP連接基礎(chǔ)上
一次TCP連接至少產(chǎn)生一次HTTP請求
連接限制
ngx_http_limit_conn_module模塊用于限制每個(gè)定義鍵的連接數(shù)皮获,特別是來自單個(gè)IP地址的連接數(shù)。
配置示例
http {
????...
? ?#對(duì)單個(gè)ip纹冤、單個(gè)會(huì)話同時(shí)存在的連接數(shù)的限制洒宝。這里定義一個(gè)存儲(chǔ)區(qū)conn_zone,conn_zone的容量是1m萌京,該存儲(chǔ)區(qū)針對(duì)于變量$binary_remote_add生效雁歌,這里是針對(duì)單個(gè)IP生效。該模塊只是一個(gè)定義知残,配置在http配置段靠瞎,需要配合limit_conn指令使用才生效,?limit_conn one 1表示該location段使用conn_zone定義的?limit_conn_zone?,對(duì)單個(gè)IP限制同時(shí)存在一個(gè)連接较坛。
? ? limit_conn_zone $binary_remote_addr zone=conn_zone:1m;
? ? server {
????????????location / {
? ? ? ? ? ? ? ? ? ? limit_conn conn_zone 1;
? ????????? }
}
做個(gè)演示:
未開啟連接限制時(shí)做個(gè)壓力測試 (不懂a(chǎn)b的可以看看https://www.cnblogs.com/TingJie/articles/4974885.html這個(gè)文章 , 很簡單明了)
ab -n 10000 -c 1000 http://192.168.58.100/index.html?
Complete requests:? ? ? 10000
Failed requests:? ? ? ? 78
這里模擬了10萬個(gè)請求 , 1000個(gè)并發(fā) , 78個(gè)請求失敗,打開nginx的錯(cuò)誤日志查看都是打開文件失敗的錯(cuò)誤 ( open() "/usr/share/nginx/html/50x.html" failed (24: Too many open files))! 想象一下 , 如果我們是一個(gè)商城程序的API接口 , 正常情況下 , 同一個(gè)IP下10萬個(gè)請求1000個(gè)并發(fā) , 算不算惡意攻擊?那么就需要做一下連接限制了噻 , 具體怎么限制根絕具體的邏輯去處理 , 我們這里簡單的限制一下(啟用配置示例的連接限制)再次做個(gè)壓力測試:
ab -n 10000 -c 1000 http://192.168.58.100/index.html?
Complete requests:? ? ? 100000
Failed requests:? ? ? ? 43616
開啟連接限制對(duì)單個(gè)IP限制同時(shí)只能存在一個(gè)連接,這里模擬了10萬個(gè)請求 , 1000個(gè)并發(fā) , 43616個(gè)請求失敗,打開nginx的錯(cuò)誤日志查看下錯(cuò)誤全是連接限制的作用(limiting connections by zone "conn_zone") ,?我們知道"現(xiàn)在http1.1以上的版本已經(jīng)可以實(shí)現(xiàn)一次建立http的連接進(jìn)行多次的http的request和response(請求和響應(yīng))?" , 大家想想 , 如果我們需要做一個(gè)搶購和秒殺 , 是不是需要對(duì)單個(gè)搶購和秒殺限制連接單個(gè)IP同時(shí)只能存在一個(gè)或者多個(gè)連接的限制?不然人家寫個(gè)腳本程序運(yùn)行在十臺(tái)八臺(tái)的機(jī)器上瘋狂的請求怎么辦?當(dāng)然這只是一個(gè)比較簡單的應(yīng)用場景 , 更多的還是需要自己思考與摸索.
請求限制
ngx_http_limit_req_module模塊用于限制請求的處理速率印蔗,特別是單一的IP地址的請求的處理速率扒最。使用“漏桶”方法進(jìn)行限制丑勤。
配置示例
http {
????...
? ?#$binary_remote_addr表示的是客戶端的地址,zone=req_zone:1m代表的是開辟了一個(gè)名為req_zone的1M的空間,1M的空間可以存儲(chǔ)多少個(gè)$binary_remote_addr這里不解釋了 , Nginx官網(wǎng)文檔介紹的相當(dāng)清除 , 速率rate=1r/s代表的是每秒1個(gè) , 所以這里定義的配置代表:對(duì)于同一ip的請求,限制平均速率為1個(gè)請求/秒吧趣。
? ? limit_req_zone $binary_remote_addr zone=req_zone:1m rate=1r/s;
????server {
? ? ? ? ? ? ...
? ? ????????location / {
? ? ? ????????????? root? /usr/share/nginx/html;
? ? ? ????????????? index? index.html index.htm;
? ? ? ? ? ? ? ? ? ? #請求限制 : 對(duì)于符合名為req_zone的limit_req_zone 配置(對(duì)于同一ip的請求法竞,限制平均速率為1個(gè)請求/秒) ,?超過部分進(jìn)行延遲處理,若超過3個(gè)請求/秒强挫,丟棄超過部分岔霸。
????????????????????#limit_req zone=req_zone burst=3 nodelay;
? ? ? ? ? ? ? ? ? ? #請求限制 :?對(duì)于符合名為req_zone的limit_req_zone 配置?,超過部分進(jìn)行延遲處理,若超過3個(gè)請求/秒俯渤,所有請求都被過度延遲,直到名為req_zone的limit_req_zone 配置設(shè)置的1M存儲(chǔ)區(qū)被占滿,如果存儲(chǔ)區(qū)耗盡呆细,則刪除最近最少使用的狀態(tài)。即使在此之后無法創(chuàng)建新狀態(tài)八匠,請求也會(huì)因錯(cuò)誤而終止絮爷。
????????????????????#limit_req zone=req_zone burst=3;
????????????????????#請求限制 :?對(duì)于符合名為req_zone的limit_req_zone 配置(對(duì)于同一ip的請求,限制平均速率為1個(gè)請求/秒) 若超過1個(gè)請求/秒梨树,所有請求都被過度延遲,直到名為req_zone的limit_req_zone 配置設(shè)置的1M存儲(chǔ)區(qū)被占滿,如果存儲(chǔ)區(qū)耗盡坑夯,則刪除最近最少使用的狀態(tài)。即使在此之后無法創(chuàng)建新狀態(tài)抡四,請求也會(huì)因錯(cuò)誤而終止柜蜈。
????????????????????#limit_req zone=req_zone;
? ? ????????}
基本指令
limit_req_zone
語法:limit_req_zone?key zone=name:size rate=rate;
只能在http塊中使用
此指令用于聲明請求限制zone,zone可以保存各種key的狀態(tài)指巡,name是zone的唯一標(biāo)識(shí)淑履,size代表zone的內(nèi)存大小,rate指定速率限制藻雪。
參數(shù)詳解:
1.key秘噪,
若客戶的請求匹配了key,則進(jìn)入zone阔涉±峦蓿可以是文本、變量瑰排,通常為Nginx變量贯要。
如$binary_remote_addr(客戶的ip),$uri(不帶參數(shù)的請求地址)椭住,$request_uri(帶參數(shù)的請求地址)崇渗,$server_name(服務(wù)器名稱)。
支持組合使用,使用空格隔開宅广。
2.zone
使用zone=test葫掉,指定此zone的名字為test。
3.size
在zone=name后面緊跟:size跟狱,指定此zone的內(nèi)存大小俭厚。如zone=name:10m,代表name的共享內(nèi)存大小為10m驶臊。通常情況下挪挤,1m可以保存16000個(gè)狀態(tài)。
4.rate
使用rate=1r/s关翎,限制平均1秒不超過1個(gè)請求扛门。使用rate=1r/m,限制平均1分鐘不超過1個(gè)請求纵寝。
例子:
limit_req_zone $binary_remote_addr zone=one:10m rate=5r/s;
limit_req_zone $binary_remote_addr $uri zone=two:10m rate=1r/s;
同一ip不同請求地址论寨,進(jìn)入名為one的zone,限制速率為5請求/秒爽茴。
同一ip同一請求地址葬凳,進(jìn)入名為two的zone,限制速率為1請求/秒闹啦。
limit_req?zone
語法:limit_req?zone=name [burst=number] [nodelay];
可在http, server, location塊中使用
此指令用于設(shè)置共享的內(nèi)存zone和最大的突發(fā)請求大小沮明。
若請求速率超過了limit_req_zone中指定的rate但小于limit_req中的burst,則進(jìn)行延遲處理窍奋,若再超過burst荐健,就可以通過設(shè)置nodelay對(duì)其進(jìn)行丟棄處理。
參數(shù)詳解:
1.zone
使用zone=name指定使用名為name的zone琳袄,這個(gè)zone之前使用limit_req_zone聲明過江场。
2.burst(可選)
burst用于指定最大突發(fā)請求數(shù)。許多場景下窖逗,單一地限制rate并不能滿足需求址否,設(shè)置burst,可以延遲處理超過rate限制的請求碎紊。
3.nodelay(可選)
如果設(shè)置了nodelay佑附,在突發(fā)請求數(shù)大于burst時(shí),會(huì)丟棄掉這部分請求仗考。因?yàn)槿绻皇茄舆t處理音同,就像”漏斗“,一旦上面加得快(請求)秃嗜,下面漏的慢(處理速度)权均,”漏斗“總會(huì)有溢出的時(shí)候顿膨。這時(shí),丟棄掉溢出的部分就顯得很有意義了叽赊。
單客戶分為三種情況:
請求速率 < rate(1r/s)恋沃,正常處理
rate(1r/s) < 請求速率 < burst(5r/s),大于rate部分延遲
burst(5r/s)? < 請求速率必指,大于burst部分丟棄(返回503服務(wù)暫時(shí)不可用)
做個(gè)演示:
未開啟請求限制時(shí)做個(gè)壓力測試 (不懂a(chǎn)b的可以看看https://www.cnblogs.com/TingJie/articles/4974885.html這個(gè)文章 , 很簡單明了)
ab -n 100000 -c 1000 http://192.168.58.100/index.html?
Complete requests:? ? ? 10000
Failed requests:? ? ? ? 0
這里模擬了10萬個(gè)請求 , 1000個(gè)并發(fā) , 全部請求成功! 想想一下 , 如果我們是一個(gè)商城程序的API接口 , 正常情況下,同一個(gè)IP下10萬個(gè)請求算不算惡意攻擊?那么就需要做一下請求限制了噻 , 具體怎么限制根絕具體的邏輯去處理 , 我們這里簡單的限制一下:
同一ip請求囊咏,進(jìn)入名為req_zone的zone,限制速率為20次請求/秒,
超過部分進(jìn)行延遲處理取劫,若超過10個(gè)請求/秒匆笤,丟棄超過部分研侣。
http {
? ? limit_req_zone $binary_remote_addr zone=req_zone:1m rate=20r/s;
????server {
? ? ? ? ? ? ...
? ? ????????location / {
? ? ? ? ? ? ? ? ? ? ...
????????????????????#limit_req zone=req_zone burst=10 nodelay;
? ? ????????}
修改了配置之后平滑重啟一下nginx -s reload , 再次使用之前的參數(shù)做個(gè)壓力測試看看
同一IP發(fā)起了請求10萬次, nginx只接受處理了100次,是不是nginx的壓力一下子就小了
Complete requests: 100000
Failed requests:? ? ? ? 99900