iT邦幫忙

2021 iThome 鐵人賽

DAY 8
1
Security

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

Day08-流量限制(三)

前言

昨天有講到怎麼運用 nginx 來做基本的分流、限流,但因為昨天舉的例子比較簡單,真實世界的應用可能會更複雜,所以今天要針對限流的部分講一些真實世界可能會遇到的例子~

針對不同的 endpoint 做限制

就如前天提到的,既然每個 API endpoint 要做的事情不一樣,所需要的資源(CPU、時間、$$$)也會不同,所以針對所有 API 都做相同的流量限制顯然不太合理,應該要針對高成本的 API(如發手機驗證簡訊、重設密碼等等)做更嚴格的流量限制

那具體而言要怎麼做呢?首先是你可以一次宣告多個 zone,譬如說用來限制「發送手機驗證簡訊」的 limit_verifyPhone、限制「重設密碼」 limit_resetPassword 等等,接著根據他們的 API 路徑把套用上去

http {
    limit_req_zone $binary_remote_addr zone=limit_verifyPhone:10m rate=2r/m;
    limit_req_zone $binary_remote_addr zone=limit_resetPassword:10m rate=1r/m;

    server {
        location /api/verifyPhone {
            limit_req zone=limit_verifyPhone;
            proxy_pass http://backend;
        }  
        location /api/resetPassword {
            limit_req zone=limit_resetPassword;
            proxy_pass http://backend;
        }
    }
}

因為 nginx 會從最長的路徑開始匹配(路徑越長優先權越高),因此如果有 /api/resetPassword/api/verifyPhone 的請求進來,就會直接被對應的 location 抓走,也就會受到 limit_resetPasswordlimit_verifyPhone 的限制。反之如果不是這兩個路徑,那才會被優先權最低的 location /api 給撿走,沒有經過任何流量限制就直接轉發到後端伺服器

queue 起來或是直接拒絕?

了解怎麼對不同 endpoint 做限制後,接著我們來探討另外一個問題:你希望過量的請求是先被 queue 起來待會再處理?還是直接拒絕掉?

這問題其實沒有標準答案,端看你的需求。如果你希望所有請求最後都能成功被處理,就算多等一下也無所謂,那就可以先 queue 起來;反之如果你擔心一下子來太多請求會無法在短時間內處理完,那就可以先把過量的請求拒絕掉,讓 client 端知道他發太多請求了。不過不管你想要的是哪一種處理方式,nginx 都可以幫你做到,只不過沒特別設定的話 nginx 預設行為是把過量的請求直接拒絕掉。

那如果想要把過量的請求 queue 起來的話,應該要怎麼設定呢?很簡單,只要在 limit_req 的結尾加上 burst=<queue size> 就可以了。假如你設定的限流是「每分鐘最多 100 個請求」,而且設定 burst=200,在這情況下一分鐘內突然來了 300 個請求,那 nginx 就會把前 200 個請求以「平均每分鐘一百個的速度」慢慢推送給 API server,至於那多出來的 100 個過量請求就直接跟他們 say goodbye

http {
    limit_req_zone $binary_remote_addr zone=limit_verifyPhone:10m rate=3r/m;
    limit_req_zone $binary_remote_addr zone=limit_resetPassword:10m rate=1r/m;

    server {
        location /api/verifyPhone {
            # 如果一下子來太多電話認證的請求,就先 queue 起來慢慢處理,避免一下子發出太多簡訊
            limit_req zone=limit_verifyPhone burst=5;
            proxy_pass http://backend;
        }  
        location /api/resetPassword {
            limit_req zone=limit_resetPassword;
            proxy_pass http://backend;
        }
    }
}

而根據使用情境不同,你也可以只挑部分 API 設定 burst,像上面的例子就只有在 /api/verifyPhone 設定 burst=5,如果來的是大量 /api/resetPassword 的請求,那還是會直接被回絕掉

小結

今天對於 nginx 限流的部分講了一些更複雜的例子,相信大家看到這邊應該都更了解怎麼在 nginx 做限流了,但關於 nginx burst 的部分還沒完全講完,明天還會做一些小補充。如果對於今天的內容有什麼問題歡迎在下方留言,都沒問題的話就明天見囉~


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

尚未有邦友留言

立即登入留言