架構必備:Rate limiting 的作用和常見方式
Rate limiting 在 Web 架構中非常重要,是互聯網架構可靠性保證重要的一個方面。
從最終用戶訪問安全的角度看,設想有人想暴力碰撞網站的用戶密碼;或者有人攻擊某個很耗費資源的接口;或者有人想從某個接口大量抓取數據。大部分 人都知道應該增加 Rate limiting,做請求頻率限制。從安全角度,這個可能也是大部分能想到,但不一定去做的薄弱環節。
從整個架構的穩定性角度看,一般 SOA 架構的每個接口的有限資源的情況下,所能提供的單位時間服務能力是有限的。假如超過服務能力,一般會造成整個接口服務停頓,或者應用 Crash,或者帶來連鎖反應,將延遲傳遞給服務調用方造成整個系統的服務能力喪失。有必要在服務能力超限的情況下 Fail Fast。
另外,根據排隊論,由于 API 接口服務具有延遲隨著請求量提升迅速提升的特點,為了保證 SLA 的低延遲,需要控制單位時間的請求量。這也是 Little’s law 所說的。
還有,公開 API 接口服務,Rate limiting 應該是一個必備的功能,否則公開的接口不知道哪一天就會被服務調用方有意無意的打垮。
所以,提供資源能夠支撐的服務,將過載請求快速拋棄對整個系統架構的穩定性非常重要。這就要求在應用層實現 Rate limiting 限制。
常見的 Rate limiting 的實現方式
Proxy 層的實現,針對部分 URL 或者 API 接口進行訪問頻率限制
Nginx 模塊
limit_req_zone $binary_remote_addr zone=one:10m rate=1r/s; server { location /search/ { limit_req zone=one burst=5; }
詳細參見: ngx_http_limit_req_module
Haproxy 提供的功能
詳細參見: Haproxy Rate limit 模塊
Java、Scala JVM 系應用層實現
Google Guava 提供了一個 RateLimiter 實現。使用方式簡單明了,在自己的應用中簡單封裝即可,放到 HTTP 服務或者其他邏輯接口調用的前端。
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); } }
詳細參見: Google Guava RateLimiter
基于 Redis 功能的實現
這個在 Redis 官方文檔有非常詳細的實現。一般適用于所有類型的應用,比如 PHP、Python 等等。Redis 的實現方式可以支持分布式服務的訪問頻率的集中控制。Redis 的頻率限制實現方式還適用于在應用中無法狀態保存狀態的場景。