Nginx的請求限制
在配置nginx的過程中我們需要考慮受到攻擊或惡意請求的情況,比如單用戶惡意發起大量請求,這時Nginx的請求限制可以幫助我們對其進行限制。
連接頻率限制 : limit_conn_module
請求頻率限制 : limit_req_module
理解:連接頻率限制和請求頻率限制都可以實現Nginx的請求限制 , 但是他們的實現原理是不一樣的 , 區別就在于連接和請求上 , http協議的鏈接與請求 , http協議是建立在tcp協議之上的,要完成一次http的請求,先要進行tcp的3次握手建立http的連接 , 然后才進行http的request和response(請求和響應) , 現在http1.1以上的版本已經可以實現一次建立http的連接進行多次的http的request和response(請求和響應) ,最后客戶端和服務端不斷的來發送FIN包和ACK包來保持HTTP的連接 。
如果面對搶購和秒殺需求來限制 , 個人覺得連接頻率限制和請求頻率限制應該配合使用 , 使用連接頻率限制同一IP同時只能有3個連接, 再使用請求頻率限制對于同一ip的請求,限制平均速率為5個請求/秒 , 這樣是不是比單獨只使用一種限制要好很多?
比如只使用連接頻率限制 , 由于一次建立http的連接可以進行多次的請求和響應 , 我們無法精確的限制同一ip同時發起多少次的http請求 ;
比如只使用請求頻率限制 , 可以精確的限制同一ip1秒只能發起5次的http請求 , 假如同一ip1秒內發起了100000次請求 , 雖然限制了只有5次成功響應 , 但是其他的99995次的請求TCP握手建立http連接是不是會消耗服務器資源?
所以,個人覺得連接頻率限制和請求頻率限制應該配合使用!
HTTP協議的連接與請求
HTTP請求建立在一次TCP連接基礎上
一次TCP連接至少產生一次HTTP請求
連接限制
ngx_http_limit_conn_module模塊用于限制每個定義鍵的連接數,特別是來自單個IP地址的連接數。
配置示例
http {
????...
? ?#對單個ip、單個會話同時存在的連接數的限制。這里定義一個存儲區conn_zone,conn_zone的容量是1m,該存儲區針對于變量$binary_remote_add生效,這里是針對單個IP生效。該模塊只是一個定義,配置在http配置段,需要配合limit_conn指令使用才生效,?limit_conn one 1表示該location段使用conn_zone定義的?limit_conn_zone?,對單個IP限制同時存在一個連接。
? ? limit_conn_zone $binary_remote_addr zone=conn_zone:1m;
? ? server {
????????????location / {
? ? ? ? ? ? ? ? ? ? limit_conn conn_zone 1;
? ????????? }
}
做個演示:
未開啟連接限制時做個壓力測試 (不懂ab的可以看看https://www.cnblogs.com/TingJie/articles/4974885.html這個文章 , 很簡單明了)
ab -n 10000 -c 1000 http://192.168.58.100/index.html?
Complete requests:? ? ? 10000
Failed requests:? ? ? ? 78
這里模擬了10萬個請求 , 1000個并發 , 78個請求失敗,打開nginx的錯誤日志查看都是打開文件失敗的錯誤 ( open() "/usr/share/nginx/html/50x.html" failed (24: Too many open files))! 想象一下 , 如果我們是一個商城程序的API接口 , 正常情況下 , 同一個IP下10萬個請求1000個并發 , 算不算惡意攻擊?那么就需要做一下連接限制了噻 , 具體怎么限制根絕具體的邏輯去處理 , 我們這里簡單的限制一下(啟用配置示例的連接限制)再次做個壓力測試:
ab -n 10000 -c 1000 http://192.168.58.100/index.html?
Complete requests:? ? ? 100000
Failed requests:? ? ? ? 43616
開啟連接限制對單個IP限制同時只能存在一個連接,這里模擬了10萬個請求 , 1000個并發 , 43616個請求失敗,打開nginx的錯誤日志查看下錯誤全是連接限制的作用(limiting connections by zone "conn_zone") ,?我們知道"現在http1.1以上的版本已經可以實現一次建立http的連接進行多次的http的request和response(請求和響應)?" , 大家想想 , 如果我們需要做一個搶購和秒殺 , 是不是需要對單個搶購和秒殺限制連接單個IP同時只能存在一個或者多個連接的限制?不然人家寫個腳本程序運行在十臺八臺的機器上瘋狂的請求怎么辦?當然這只是一個比較簡單的應用場景 , 更多的還是需要自己思考與摸索.
請求限制
ngx_http_limit_req_module模塊用于限制請求的處理速率,特別是單一的IP地址的請求的處理速率。使用“漏桶”方法進行限制。
配置示例
http {
????...
? ?#$binary_remote_addr表示的是客戶端的地址,zone=req_zone:1m代表的是開辟了一個名為req_zone的1M的空間,1M的空間可以存儲多少個$binary_remote_addr這里不解釋了 , Nginx官網文檔介紹的相當清除 , 速率rate=1r/s代表的是每秒1個 , 所以這里定義的配置代表:對于同一ip的請求,限制平均速率為1個請求/秒。
? ? 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;
? ? ? ? ? ? ? ? ? ? #請求限制 : 對于符合名為req_zone的limit_req_zone 配置(對于同一ip的請求,限制平均速率為1個請求/秒) ,?超過部分進行延遲處理,若超過3個請求/秒,丟棄超過部分。
????????????????????#limit_req zone=req_zone burst=3 nodelay;
? ? ? ? ? ? ? ? ? ? #請求限制 :?對于符合名為req_zone的limit_req_zone 配置?,超過部分進行延遲處理,若超過3個請求/秒,所有請求都被過度延遲,直到名為req_zone的limit_req_zone 配置設置的1M存儲區被占滿,如果存儲區耗盡,則刪除最近最少使用的狀態。即使在此之后無法創建新狀態,請求也會因錯誤而終止。
????????????????????#limit_req zone=req_zone burst=3;
????????????????????#請求限制 :?對于符合名為req_zone的limit_req_zone 配置(對于同一ip的請求,限制平均速率為1個請求/秒) 若超過1個請求/秒,所有請求都被過度延遲,直到名為req_zone的limit_req_zone 配置設置的1M存儲區被占滿,如果存儲區耗盡,則刪除最近最少使用的狀態。即使在此之后無法創建新狀態,請求也會因錯誤而終止。
????????????????????#limit_req zone=req_zone;
? ? ????????}
基本指令
limit_req_zone
語法:limit_req_zone?key zone=name:size rate=rate;
只能在http塊中使用
此指令用于聲明請求限制zone,zone可以保存各種key的狀態,name是zone的唯一標識,size代表zone的內存大小,rate指定速率限制。
參數詳解:
1.key,
若客戶的請求匹配了key,則進入zone。可以是文本、變量,通常為Nginx變量。
如$binary_remote_addr(客戶的ip),$uri(不帶參數的請求地址),$request_uri(帶參數的請求地址),$server_name(服務器名稱)。
支持組合使用,使用空格隔開。
2.zone
使用zone=test,指定此zone的名字為test。
3.size
在zone=name后面緊跟:size,指定此zone的內存大小。如zone=name:10m,代表name的共享內存大小為10m。通常情況下,1m可以保存16000個狀態。
4.rate
使用rate=1r/s,限制平均1秒不超過1個請求。使用rate=1r/m,限制平均1分鐘不超過1個請求。
例子:
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不同請求地址,進入名為one的zone,限制速率為5請求/秒。
同一ip同一請求地址,進入名為two的zone,限制速率為1請求/秒。
limit_req?zone
語法:limit_req?zone=name [burst=number] [nodelay];
可在http, server, location塊中使用
此指令用于設置共享的內存zone和最大的突發請求大小。
若請求速率超過了limit_req_zone中指定的rate但小于limit_req中的burst,則進行延遲處理,若再超過burst,就可以通過設置nodelay對其進行丟棄處理。
參數詳解:
1.zone
使用zone=name指定使用名為name的zone,這個zone之前使用limit_req_zone聲明過。
2.burst(可選)
burst用于指定最大突發請求數。許多場景下,單一地限制rate并不能滿足需求,設置burst,可以延遲處理超過rate限制的請求。
3.nodelay(可選)
如果設置了nodelay,在突發請求數大于burst時,會丟棄掉這部分請求。因為如果只是延遲處理,就像”漏斗“,一旦上面加得快(請求),下面漏的慢(處理速度),”漏斗“總會有溢出的時候。這時,丟棄掉溢出的部分就顯得很有意義了。
單客戶分為三種情況:
請求速率 < rate(1r/s),正常處理
rate(1r/s) < 請求速率 < burst(5r/s),大于rate部分延遲
burst(5r/s)? < 請求速率,大于burst部分丟棄(返回503服務暫時不可用)
做個演示:
未開啟請求限制時做個壓力測試 (不懂ab的可以看看https://www.cnblogs.com/TingJie/articles/4974885.html這個文章 , 很簡單明了)
ab -n 100000 -c 1000 http://192.168.58.100/index.html?
Complete requests:? ? ? 10000
Failed requests:? ? ? ? 0
這里模擬了10萬個請求 , 1000個并發 , 全部請求成功! 想想一下 , 如果我們是一個商城程序的API接口 , 正常情況下,同一個IP下10萬個請求算不算惡意攻擊?那么就需要做一下請求限制了噻 , 具體怎么限制根絕具體的邏輯去處理 , 我們這里簡單的限制一下:
同一ip請求,進入名為req_zone的zone,限制速率為20次請求/秒,
超過部分進行延遲處理,若超過10個請求/秒,丟棄超過部分。
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 , 再次使用之前的參數做個壓力測試看看
同一IP發起了請求10萬次, nginx只接受處理了100次,是不是nginx的壓力一下子就小了
Complete requests: 100000
Failed requests:? ? ? ? 99900