昨天 Day08 時有跟大家介紹 nginx limit_req
裡面的 burst
參數該怎麼使用,簡單來說就是用來設定 queue 的長度,若 queue 爆了就直接拒絕。而今天要講的是另一個參數 delay
,跟 burst
合在一起使用可以做出很多不同的變化哦~
考慮以下這個設定檔,譬如說我希望平均而言每秒最多就一個 /api
開頭的請求進來,所以設定 1r/s,又因為 burst=10
的關係,所以最多可以允許 10 個請求同時進來排隊(第 11 個就直接拜拜),nginx 再以 1r/s 的速度慢慢轉送給 API server,在 10 秒內把請求處理完
http {
# 每秒最多一個請求 1r/s
limit_req_zone $binary_remote_addr zone=limit:10m rate=1r/s;
server {
location /api {
# queue 的長度是 10
limit_req zone=limit burst=10;
proxy_pass http://backend;
}
}
}
如果畫成圖的話就長這樣,一開始有很多個請求同時進來,因為不超過十個,所以他們會被 queue 起來,再以 1r/s 的速度往 API server 送
但這樣有個壞處,譬如說很多時候前端網站為了提升使用者體驗,可能會同時發起多個請求(GET /api/aaa
、GET /api/bbb
、GET /api/ccc
),並且預期他們會被平行處理(這樣才能提高效率嘛)。但由於 nginx 1r/s 的限制,就算你同時發了三個請求到後端,最後一個請求 GET /api/ccc
還是要先排隊兩秒才會開始被處理,因此會大幅降低效率
那有沒有什麼方法既可以做到限流,但又不要限得這麼死呢?解法就是在 limit_req
的最後加上 nodelay
,來看看以下這個設定檔,除了加上 nodelay
之外其他部分都長得一樣,而這邊的 nodelay
可以把限制稍微放寬一點
http {
limit_req_zone $binary_remote_addr zone=limit:10m rate=1r/s;
server {
location /api {
limit_req zone=limit burst=10 nodelay; # HERE
proxy_pass http://backend;
}
}
}
怎麼個放寬法呢?在加上 nodelay
之前,因為 rate=1r/s
的關係,所以「每秒只能有一個請求被轉送到 API server」,但有了 burst=10 nodelay
之後,就變成「每 10 秒最多可以有 10 個請求被轉送到 API server」,雖然平均而言還是一秒一個,但就多了許多彈性
畫成圖的話,假如你在連續幾秒內一口氣送了 11 個請求,因為 burst=10 nodelay
的關係,前 10 請求個會被馬上送進去 API server 處理,第 11 個請求則是等到 10 秒過後才會被送進去(畢竟每 10 秒最多只能有 10 個請求)
因爲加上 nodelay
後就可以同時處理多個請求,所以可以成功解決上面提到的「前端同時發起多個請求的問題」,不會再需要每個請求都排隊,在限流的同時也兼顧到了效能,是非常好的做法哦~
從前天開始總共花了三天講怎麼在 nginx 做限流,這幾天的內容比較複雜一點,如果不熟悉 nginx 的朋友們讀起來可能會覺得有些吃力,但願意認真花時間下去了解的話會發現 nginx 的限流真的非常好用,不只可以保護你的 API server,而且效能上的表現也很不錯。所以如果看不太懂的話可以多看幾遍,真有什麼看不懂的地方也可以在下方留言,我會盡量回答大家的問題的~