系統(tǒng)設計時一般會預估負載,當系統(tǒng)遭受惡意攻擊或正常突發(fā)流量等都可能導致系統(tǒng)被壓垮,而限流就是保護措施之一之景。
一、限流算法介紹
令牌桶算法
算法思想是:
- 令牌以固定速率產(chǎn)生膏潮,并緩存到令牌桶中锻狗;
- 令牌桶放滿時,多余的令牌被丟棄焕参;
- 請求要消耗等比例的令牌才能被處理轻纪;
- 令牌不夠時,請求被緩存叠纷。
漏桶算法
算法思想是:
- 水(請求)從上方倒入水桶刻帚,從水桶下方流出(被處理);
- 來不及流出的水存在水桶中(緩沖)涩嚣,以固定速率流出崇众;
- 水桶滿后水溢出(丟棄)。
這個算法的核心是:緩存請求航厚、勻速處理顷歌、多余的請求直接丟棄。
漏桶和令牌桶算法最明顯的區(qū)別在于是否允許突發(fā)流量(burst)的處理幔睬,漏桶算法能夠強行限制數(shù)據(jù)的實時傳輸(處理)速率衙吩,對突發(fā)流量不做額外處理;而令牌桶算法能夠在限制數(shù)據(jù)的平均傳輸速率的同時允許某種程度的突發(fā)傳輸溪窒。
二、Nginx 限流
Nginx 提供兩種限流方式冯勉,一是控制速率澈蚌,二是控制并發(fā)連接數(shù):
-
limit_req_zone
模塊用來限制單位時間內(nèi)的請求數(shù),即速率限制灼狰,采用的漏桶算法 "leaky bucket"宛瞄。 -
limit_req_conn
模塊用來限制同一時間連接數(shù),即并發(fā)限制。
控制速率
ngx_http_limit_req_module 模塊提供限制請求處理速率能力份汗,它能夠有效針對同一個 IP 反復請求服務器盈电,如洪水攻擊或者 DDos 攻擊。下面例子使用 nginx 的 limit_req_zone 和 limit_req 兩個指令杯活,限制單個IP的請求處理速率匆帚。
在 nginx.conf 的 http 域中添加限流配置:
# 格式:limit_req_zone key zone rate
http {
limit_req_zone $binary_remote_addr zone=myRateLimit:10m rate=10r/s;
}
# 配置 server,使用 limit_req 指令應用限流旁钧。
server {
location / {
limit_req zone=myRateLimit;
proxy_pass http://my_upstream;
}
}
- $binary_remote_addr:定義限流對象吸重,binary_remote_addr 是一種 key,表示基于 remote_addr(客戶端 IP) 來做限流歪今,binary_ 的目的是壓縮內(nèi)存占用量嚎幸。
- zone:定義共享內(nèi)存區(qū)來存儲訪問信息, myRateLimit:10m 表示一個大小為10M寄猩,名字為myRateLimit的內(nèi)存區(qū)域嫉晶。1M 內(nèi)存能存儲16000個 IP 地址的訪問信息,10M 內(nèi)存可以存儲 16W個 IP地址訪問信息田篇。
- rate:用于設置最大訪問速率替废,rate=10r/s 表示每秒最多處理10個請求。Nginx 實際上以毫秒為粒度來跟蹤請求信息斯辰,因此 10r/s 實際上是限制:每100毫秒處理一個請求舶担。這意味著,自上一個請求處理完后彬呻,若后續(xù)100毫秒內(nèi)又有請求到達衣陶,將拒絕處理該請求。
-
zone=myRateLimit
設置使用哪個配置區(qū)域來做限制闸氮,與 limit_req_zone 里的配置對應
處理突發(fā)流量
上面例子限制 10r/s剪况,正常流量稍微增大,請求就會被拒絕蒲跨,面對突發(fā)流量译断,可以結(jié)合 burst 參數(shù)使用來解決該問題。
server {
location / {
limit_req zone=myRateLimit burst=20;
proxy_pass http://my_upstream;
}
}
burst 譯為突發(fā)或悲、爆發(fā)孙咪,表示在超過設定的處理速率后能額外處理的請求數(shù)。當 rate=10r/s 時巡语,將1s拆成10份翎蹈,即每100ms可處理1個請求。此處男公,burst=20荤堪,若同時有20個請求到達,Nginx 會處理第一個請求,剩余19個請求將放入隊列澄阳,然后每隔100ms從隊列中獲取一個請求進行處理拥知。若請求數(shù)大于20,將拒絕處理多余的請求碎赢,直接返回 503
不過低剔,單獨使用 burst 參數(shù)并不實用。假設 burst=50揩抡,rate依然為10r/s户侥,排隊中的50個請求雖然每100ms會處理一個,但第50個請求卻需要等待 50 * 100ms即 5s峦嗤,這么長的處理時間自然難以接受蕊唐。因此,burst 往往結(jié)合 nodelay 一起使用烁设。
server {
location / {
limit_req zone=myRateLimit burst=20 nodelay;
proxy_pass http://my_upstream;
}
}
nodelay
針對的是 burst 參數(shù)替梨,burst=20 nodelay 表示這20個請求立馬處理,不能延遲装黑,相當于特事特辦副瀑。不過,即使這20個突發(fā)請求立馬處理結(jié)束恋谭,后續(xù)來了請求也不會立馬處理糠睡。burst=20 相當于緩存隊列中占了20個坑,即使請求被處理了疚颊,這20個位置這只能按 100ms 一個來釋放狈孔。
這就達到了速率穩(wěn)定,但突然流量也能正常處理的效果材义。
限制連接數(shù)
ngx_http_limit_conn_module 提供了限制連接數(shù)的能力均抽,利用 limit_conn_zone 和 limit_conn 兩個指令即可。下面是 Nginx 官方例子:
limit_conn_zone $binary_remote_addr zone=perip:10m;
limit_conn_zone $server_name zone=perserver:10m;
server {
...
limit_conn perip 10;
limit_conn perserver 100;
}
-
limit_conn perip 10
作用的 key 是 $binary_remote_addr其掂,表示限制單個 IP 同時最多能持有10個連接油挥。 -
limit_conn perserver 100
作用的 key 是 $server_name,表示虛擬主機(server) 同時能處理并發(fā)連接的總數(shù)款熬。
需要注意的是:只有當 request header 被后端 server 處理后深寥,這個連接才進行計數(shù)。
設置白名單
限流主要針對外部訪問贤牛,內(nèi)網(wǎng)訪問相對安全翩迈,可以不做限流,通過設置白名單即可盔夜。利用 Nginx ngx_http_geo_module 和 ngx_http_map_module 兩個工具模塊即可搞定。
在 nginx.conf 的 http 部分中配置白名單:
geo $limit {
default 1; // key=default, value=1
10.0.0.0/8 0;
192.168.0.0/24 0;
172.20.0.35 0;
include conf/whiteip.conf; // 支持白名單以 key:value的形式存儲在配置文件中
}
map $limit $limit_key {
0 "";
1 $binary_remote_addr; // value=1則返回 $binary_remote_addr,也就是IP喂链,否則返回空字符串
}
limit_req_zone $limit_key zone=myRateLimit:10m rate=10r/s;
- geo 定義了子網(wǎng)或 IP 與 0返十、1 的映射關系。上述配置中椭微,10.0.0.0/8 網(wǎng)段的 IP 映射到 0洞坑,而其他 IP 缺省映射到 1;
- 來訪 IP 通過 geo 進行映射蝇率,映射結(jié)果是 0迟杂,表明該 IP 被列入白名單,經(jīng) map 轉(zhuǎn)換返回 "" 空字符串本慕;如果映射結(jié)果是1排拷,則返回 $binary_remote_addr,即客戶端實際 IP锅尘。
- limit_req_zone 限流采用的 key 不再是 binary_remote_addr监氢,而是采用 $limit_key 動態(tài)獲取值。如果是白名單藤违,limit_req_zone 的限流 key 則為空字符串浪腐,將不會限流;若不是白名單顿乒,將會對客戶端真實 IP 進行限流议街。