【轉載請註明出處】:juejin.im/post/5eacf0…html
限流並不是新鮮事,在生活中亦無處不在,下面例舉一二:node
在開發高併發系統時有三把利器用來保護系統:緩存、降級和限流nginx
經常使用的限流算法有令牌桶和和漏桶,而Google開源項目Guava中的RateLimiter使用的就是令牌桶控制算法。算法
把請求比做是水,水來了都先放進桶裏,並以限定的速度出水,當水來得過猛而出水不夠快時就會致使水直接溢出,即拒絕服務。 後端
在漏斗中沒有水的時候緩存
在漏斗中有水的時候安全
對於不少應用場景來講,除了要求可以限制數據的平均傳輸速率外,還要求容許某種程度的突發傳輸。這時候漏桶算法可能就不合適了,令牌桶算法更爲適合。 bash
令牌桶算法的原理是系統以恆定的速率產生令牌,而後把令牌放到令牌桶中,令牌桶有一個容量,當令牌桶滿了的時候,再向其中放令牌,那麼多餘的令牌會被丟棄;當想要處理一個請求的時候,須要從令牌桶中取出一個令牌,若是此時令牌桶中沒有令牌,那麼則拒絕該請求。服務器
漏桶 漏桶的出水速度是恆定的,那麼意味着若是瞬時大流量的話,將有大部分請求被丟棄掉(也就是所謂的溢出)。併發
令牌桶 生成令牌的速度是恆定的,而請求去拿令牌是沒有速度限制的。這意味,面對瞬時大流量,該算法能夠在短期內請求拿到大量令牌,並且拿令牌的過程並非消耗很大的事情。
Nginx官方版本限制IP的鏈接和併發分別有兩個模塊:
Nginx按請求速率限速模塊使用的是漏桶算法,即可以強行保證請求的實時處理速度不會超過設置的閾值。 指令
Syntax: limit_req zone=name [burst=number] [nodelay | delay=number]; Default: — Context: http, server, location
limit_req_zone $binary_remote_addr zone=one:10m rate=1r/s;
limit_req zone=one burst=5 nodelay;
爲服務器因爲超過頻次或延遲處理而拒絕處理請求的狀況設置所需的日誌記錄級別。延遲的日誌記錄級別比拒絕的日誌記錄級別低1級;例如,「limit_req_log_level notice」 的延遲日誌記錄級別是info。
Syntax: limit_req_log_level info | notice | warn | error; Default: limit_req_log_level error; Context: http, server, location
Syntax: limit_req_status code; Default: limit_req_status 503; Context: http, server, location
http {
limit_req_zone $binary_remote_addr zone=one:10m rate=1r/s;
server {
location /search/ {
limit_req zone=one;
}
}
複製代碼
限流速度爲每秒10次請求,若是有10次請求同時到達一個空閒的nginx,他們都能獲得執行嗎?
漏桶漏出請求是勻速的。10r/s是怎樣勻速的呢?每100ms漏出一個請求。在這樣的配置下,桶是空的,全部不能實時漏出的請求,都會被拒絕掉。因此若是10次請求同時到達,那麼只有一個請求可以獲得執行,其它的,都會被拒絕。 這不太友好,大部分業務場景下咱們但願這10個請求都能獲得執行,添加突發流量處理機制。
http {
limit_req_zone $binary_remote_addr zone=one:10m rate=1r/s;
server {
location /search/ {
limit_req zone=one burst=12;
}
}
複製代碼
burst=12 漏桶的大小設置爲12。
邏輯上叫漏桶,實現起來是FIFO隊列,把得不到執行的請求暫時緩存起來。這樣漏出的速度仍然是100ms一個請求,但就併發而言,暫時得不到執行的請求,能夠先緩存起來。只有當隊列滿了的時候,纔會拒絕接受新請求。這樣漏桶在限流的同時,也起到了削峯填谷的做用。
在這樣的配置下,若是有10次請求同時到達,它們會依次執行,每100ms執行1個。雖然獲得執行了,但由於排隊執行,延遲大大增長,在不少場景下仍然是不能接受的。繼續修改配置,解決Delay過久致使延遲增長的問題。
http {
limit_req_zone $binary_remote_addr zone=one:10m rate=1r/s;
server {
location /search/ {
limit_req zone=one burst=12 nodelay;
}
}
複製代碼
nodelay 把開始執行請求的時間提早,之前是delay到從桶裏漏出來才執行,如今不delay了,只要入桶就開始執行。
要麼馬上執行,要麼被拒絕,請求不會由於限流而增長延遲了。由於請求從桶裏漏出來仍是勻速的(100ms釋放1個),桶的空間又是固定的,最終平均下來,仍是每秒執行了10次請求,限流的目的仍是達到了。 可是請注意,雖然設置burst和nodelay可以下降突發請求的處理時間,可是長期來看並不會提升吞吐量的上限,長期吞吐量的上限是由rate決定的,由於nodelay只能保證burst的請求被當即處理,但Nginx會限制隊列元素釋放的速度,就像是限制了令牌桶中令牌產生的速度。
但這樣也有缺點,限流是限了,可是限得不那麼勻速。以上面的配置舉例,若是有12個請求同時到達,那麼這12個請求都可以馬上執行,而後後面的請求只能勻速進桶,100ms執行1個。若是有一段時間沒有請求,桶空了,那麼又可能出現併發的12個請求一塊兒執行。 大部分狀況下,這種限流不勻速,不算是大問題。不過nginx也提供了一個參數控制併發執行也就是nodelay的請求的數量。
http {
limit_req_zone $binary_remote_addr zone=one:10m rate=1r/s;
server {
location /search/ {
limit_req zone=one burst=12 delay=4;
}
}
複製代碼
delay=4 從桶內第5個請求開始delay
這樣經過控制delay參數的值,能夠調整容許併發執行的請求的數量,使得請求變的均勻起來,在有些耗資源的服務上控制這個數量,仍是有必要的。
這個模塊用來限制單個IP的請求數。並不是全部的鏈接都被計數。只有在服務器處理了請求而且已經讀取了整個請求頭時,鏈接才被計數。
Syntax: limit_conn zone number; Default: — Context: http, server, location
如:
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) 同時能處理併發鏈接的總數。
須要注意的是:只有當 request header 被後端server處理後,這個鏈接才進行計數。
Syntax: limit_conn_log_level info | notice | warn | error; Default: limit_conn_log_level error; Context: http, server, location
Syntax: limit_conn_status code; Default: limit_conn_status 503; Context: http, server, location
限流主要針對外部訪問,內網訪問相對安全,能夠不作限流,經過設置白名單便可。利用 Nginx ngx_http_geo_module 和 ngx_http_map_module 兩個工具模塊便可搞定。
geo $limit {
default 1;
10.0.0.0/8 0;
192.168.0.0/24 0;
172.20.0.35 0;
}
map $limit $limit_key {
0 "";
1 $binary_remote_addr;
}
limit_req_zone $limit_key zone=myRateLimit:10m rate=10r/s;
複製代碼
geo 對於白名單(子網或IP均可以) 將返回0,其餘IP將返回1。
map 將 $limit
轉換爲 $limit_key
,若是是 $limit
是0(白名單),則返回空字符串;若是是1,則返回客戶端實際IP。
limit_req_zone 限流的key再也不使用 $binary_remote_addr
,而是 $limit_key
來動態獲取值。若是是白名單,limit_req_zone 的限流key則爲空字符串,將不會限流;若不是白名單,將會對客戶端真實IP進行限流。
除限流外,ngx_http_core_module 還提供了限制數據傳輸速度的能力(即常說的下載速度)。
例如:
location /flv/ {
flv;
limit_rate_after 20m;
limit_rate 100k;
}
複製代碼
這個限制是針對每一個請求的,表示客戶端下載前20M時不限速,後續限制100kb/s。
能夠限制特定UA(好比爬蟲)的訪問
limit_req_zone $anti_spider zone=one:10m rate=10r/s;
limit_req zone=one burst=100 nodelay;
if ($http_user_agent ~* (YisouSpider|Scrapy)) {
set $anti_spider $http_user_agent;
}
複製代碼
【轉載請註明出處】:juejin.im/post/5eacf0…