為什么需要限流
如果你的后端服務器只能支持10QPS的并發(fā)量, 當用戶量突然暴漲或者有CC攻擊的時候并發(fā)量將遠遠大于這個值, 為了避免大量的請求導致后端服務器CPU/Memory異常, 或則導致后端數據庫異常而導致的崩潰, 這時候我們需要對服務進行'限流降級', 保證服務器不崩潰, 這總比崩潰之后導致所有人都不能使用好.
為Nginx配置限流
Nginx默認是不限流的, 需要我們手動配置開啟限流.
在conf配置文件的任何地方 寫下 限流規(guī)則的定義.
語法如下:
limit_req_zone key zone=name:size rate=rate [sync];
例如
limit_req_zone $binary_remote_addr zone=perip:10m rate=10r/s;
limit_req_zone $http_host zone=perhost:10m rate=10r/s;
上面的兩個指令的意思大致為:
使用 limit_req_zone 規(guī)則給每一個Ip($binary_remote_addr) 限流, 區(qū)域(即名字)為perip, 容量為10M, 限制速率為每秒10次.
其中 key 可以是內置的變量, 或者自定義的變量, 更多寫法可以參閱官方文檔: ngx_http_limit_req_module.html#limit_req_zone.
然后在 server 或者 location 上下文中使用定義的規(guī)則:
server {
listen 80;
server_name localhost;
...
limit_req zone=perip burst=5 nodelay;
limit_req zone=perhost burst=5 nodelay;
...
}
上面兩個指令的意思大致為:
使用perip區(qū)域(即名字)來限制流量, 還有兩項配置: burst 和 nodelay. 這兩個配置需要重點理解.
- burst: 譯名 爆發(fā), 意思是如果請求速率超過了配置的速率(本例中是10次/s), 則還可以將5個請求放入緩沖區(qū), 等到下一秒再處理這5個請求.
- nodelay: 表示如何處理緩存區(qū)(即burst)中的請求, 如果設置了nodelay, 則緩沖區(qū)里的請求會 都會被排隊等待處理.
深入理解 burst 和 nodelay 的作用
我們用例子繼續(xù)理解 burst 和 nodelay.
1. rate = 10r/s
即每100ms只能處理一個請求, 當在同一個時間段(如0-100ms)內有兩個請求時, 第二個請求將會被丟棄.
2. rate = 10r/s burst = 5
在上面的例子中, 我們發(fā)現當有2個請求而還達不到速率10r/s的情況下, 就會有請求被丟棄, 但現實中網絡由于有延遲, 就算用戶是勻速訪問, 都會出現多個請求同時到達服務器的情況, 這會導致大量的請求被錯誤丟棄.
我們可以使用 brust 來優(yōu)化這個問題.
現在我們將burst設置為5
- 如果在100ms以內有2個請求同時到達Nginx, 則第2個請求會先被Nginx放在緩沖區(qū), 直到下一個100ms(即100ms-200ms時間段)時處理第2個請求.
- 如果在100ms以內有7個請求同時到達Nginx, 則第2-6(共5個)個請求會先被Nginx放在緩沖區(qū), 第7個請求由于超過了緩沖區(qū)大小會被丟棄, 之后每100ms從緩沖區(qū)中取出一個請求處理, 也就是說在100ms-200ms時間段開始處理第2個請求, 并且又可以新接入第8個請求. 可以預見, 處理第6請求將會多等待 5 *100ms = 500ms 的時間, 緩存區(qū)越大, 等待時間會越長, 當然也不可能一直等待, nginx也有超時機制, 超時的請求會直接返回錯誤.
3. rate = 10r/s burst = 5 nodelay
為了解決上例中等待時間過長的問題, 還可以設置 nodelay.
和上例唯一不同的是, 當有多個請求在緩沖區(qū)時, 這多個請求將會同時被處理, 而不是每100ms處理一個.
- 如果在100ms以內有2個請求同時到達Nginx, 則第2個請求會先被Nginx放在緩沖區(qū), 并同時處理第2個請求.
- 如果在100ms以內有7個請求同時到達Nginx, 則第2-6(共5個)個請求會先被Nginx放在緩沖區(qū), 第7個請求由于超過了緩沖區(qū)大小會被丟棄, 在緩沖區(qū)中的5個請求也會被同時處理(這時有6個請求在這一時刻被nginx同時處理). 之后每100ms釋放一個緩沖區(qū)讓新的請求進來. 最后得到的結果是: 突發(fā)請求下, 在開始的0-100ms時間段內可以處理6個請求, 然后每100ms再處理一個請求, 最后速率還是會穩(wěn)定在10r/s左右.
總結
何時會丟棄請求
如果沒有配置 burst, 則在一個時區(qū)(如設置了rate=100/s, 則一個時區(qū)是10ms)內只能有一個請求, 剩下的請求都會被丟棄.
如果配置了 burst, 則在一個時區(qū)中第一個請求之后的所有請求會先放入緩沖區(qū), 當緩沖區(qū)滿了之后, 之后的請求都會被丟棄.
最佳實踐
rate + burst + nodelay 是最理想的方案, 將最大限度的保證可用, 但也會對后端服務器造成更大的壓力, 因為瞬時并發(fā)會更大. 關于如何根據后端服務器支持的QPS配置最佳的rate與burst的值, 作者還沒有仔細研究, 目前靠猜, 我的方案是 rate = burst = QPS
如果你實在害怕后端服務器被壓垮, 則使用 rate + burst 方案也不錯, 它保證每100ms只會有1個請求被處理.