架構(gòu)必備:Rate limiting 的作用和常見(jiàn)方式
Rate limiting 在 Web 架構(gòu)中非常重要,是互聯(lián)網(wǎng)架構(gòu)可靠性保證重要的一個(gè)方面。
從最終用戶(hù)訪問(wèn)安全的角度看,設(shè)想有人想暴力碰撞網(wǎng)站的用戶(hù)密碼;或者有人攻擊某個(gè)很耗費(fèi)資源的接口;或者有人想從某個(gè)接口大量抓取數(shù)據(jù)。大部分人 都知道應(yīng)該增加 Rate limiting,做請(qǐng)求頻率限制。從安全角度,這個(gè)可能也是大部分能想到,但不一定去做的薄弱環(huán)節(jié)。
從整個(gè)架構(gòu)的穩(wěn)定性角度看,一般 SOA 架構(gòu)的每個(gè)接口的有限資源的情況下,所能提供的單位時(shí)間服務(wù)能力是有限的。假如超過(guò)服務(wù)能力,一般會(huì)造成整個(gè)接口服務(wù)停頓,或者應(yīng)用 Crash,或者帶來(lái)連鎖反應(yīng),將延遲傳遞給服務(wù)調(diào)用方造成整個(gè)系統(tǒng)的服務(wù)能力喪失。有必要在服務(wù)能力超限的情況下 Fail Fast。
另外,根據(jù)排隊(duì)論,由于 API 接口服務(wù)具有延遲隨著請(qǐng)求量提升迅速提升的特點(diǎn),為了保證 SLA 的低延遲,需要控制單位時(shí)間的請(qǐng)求量。這也是 Little’s law 所說(shuō)的。
還有,公開(kāi) API 接口服務(wù),Rate limiting 應(yīng)該是一個(gè)必備的功能,否則公開(kāi)的接口不知道哪一天就會(huì)被服務(wù)調(diào)用方有意無(wú)意的打垮。
所以,提供資源能夠支撐的服務(wù),將過(guò)載請(qǐng)求快速拋棄對(duì)整個(gè)系統(tǒng)架構(gòu)的穩(wěn)定性非常重要。這就要求在應(yīng)用層實(shí)現(xiàn) Rate limiting 限制。
常見(jiàn)的 Rate limiting 的實(shí)現(xiàn)方式
Proxy 層的實(shí)現(xiàn),針對(duì)部分 URL 或者 API 接口進(jìn)行訪問(wèn)頻率限制
Nginx 模塊
limit_req_zone $binary_remote_addr zone=one:10m rate=1r/s;
server {
location /search/ {
limit_req zone=one burst=5;
}
詳細(xì)參見(jiàn):ngx_http_limit_req_module
Haproxy 提供的功能
詳細(xì)參見(jiàn):Haproxy Rate limit 模塊
Java、Scala JVM 系應(yīng)用層實(shí)現(xiàn)
Google Guava 提供了一個(gè) RateLimiter 實(shí)現(xiàn)。使用方式簡(jiǎn)單明了,在自己的應(yīng)用中簡(jiǎn)單封裝即可,放到 HTTP 服務(wù)或者其他邏輯接口調(diào)用的前端。
final RateLimiter rateLimiter = RateLimiter.create(2.0); // rate is "2 permits per second" void submitTasks(List<Runnable> tasks, Executor executor) { for (Runnable task : tasks) { rateLimiter.acquire(); // may wait executor.execute(task); } }
詳細(xì)參見(jiàn):Google Guava RateLimiter
基于 Redis 功能的實(shí)現(xiàn)
這個(gè)在 Redis 官方文檔有非常詳細(xì)的實(shí)現(xiàn)。一般適用于所有類(lèi)型的應(yīng)用,比如 PHP、Python 等等。Redis 的實(shí)現(xiàn)方式可以支持分布式服務(wù)的訪問(wèn)頻率的集中控制。Redis 的頻率限制實(shí)現(xiàn)方式還適用于在應(yīng)用中無(wú)法狀態(tài)保存狀態(tài)的場(chǎng)景。
參見(jiàn):Redis INCR rate limiter