iT邦幫忙

2021 iThome 鐵人賽

DAY 9
3
Security

從以卵擊石到堅若磐石之 Web API 安全性全攻略系列 第 9

Day09-流量限制(四)

前言

昨天 Day08 時有跟大家介紹 nginx limit_req 裡面的 burst 參數該怎麼使用,簡單來說就是用來設定 queue 的長度,若 queue 爆了就直接拒絕。而今天要講的是另一個參數 delay,跟 burst 合在一起使用可以做出很多不同的變化哦~

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 送

burst

但這樣有個壞處,譬如說很多時候前端網站為了提升使用者體驗,可能會同時發起多個請求GET /api/aaaGET /api/bbbGET /api/ccc),並且預期他們會被平行處理(這樣才能提高效率嘛)。但由於 nginx 1r/s 的限制,就算你同時發了三個請求到後端,最後一個請求 GET /api/ccc 還是要先排隊兩秒才會開始被處理,因此會大幅降低效率

nodelay

那有沒有什麼方法既可以做到限流,但又不要限得這麼死呢?解法就是在 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

因爲加上 nodelay 後就可以同時處理多個請求,所以可以成功解決上面提到的「前端同時發起多個請求的問題」,不會再需要每個請求都排隊,在限流的同時也兼顧到了效能,是非常好的做法哦~

小結

從前天開始總共花了三天講怎麼在 nginx 做限流,這幾天的內容比較複雜一點,如果不熟悉 nginx 的朋友們讀起來可能會覺得有些吃力,但願意認真花時間下去了解的話會發現 nginx 的限流真的非常好用,不只可以保護你的 API server,而且效能上的表現也很不錯。所以如果看不太懂的話可以多看幾遍,真有什麼看不懂的地方也可以在下方留言,我會盡量回答大家的問題的~


上一篇
Day08-流量限制(三)
下一篇
Day10-流量限制(五)
系列文
從以卵擊石到堅若磐石之 Web API 安全性全攻略30

尚未有邦友留言

立即登入留言