前面我們已經加入 Load Balancer、Cache、Database
Replication、Sharding、CDN、Message Queue。
但如果某個 Client 突然送出:
100,000 Requests/sec
原因可能是 Bot、Buggy Client、Crawler、Malicious User 或 Traffic
Spike。即使 Backend 可以 Scale,系統仍可能被壓垮。
今天要解決:
一個 User / Client 在一段時間內,到底可以呼叫 API 幾次?
答案就是 Rate Limiter。
Rate 是某件事情在一段時間內發生多少次。
100 requests / second
= 100 req/s
Request Rate 就是 Client 在一段時間內送出多少 Requests。
User A → 2 req/s
User B → 10 req/s
Bot → 10,000 req/s
Threshold 是限制值 / 門檻。
例如:
100 requests / minute
第 1~100 個 Request 可以 Allow,第 101 個就可能 Reject。
Rate Limiter:
限制某個 Client 在特定時間內可以執行多少次操作的機制。
User
↓
Rate Limiter
↓
Allowed?
├── Yes → Backend
└── No → Reject
常見目的:
Protect Backend
Protect Database
Prevent Abuse
Control Cost
Fair Usage
Prevent Brute Force
超過限制時,Server 常回:
HTTP 429 Too Many Requests
200 → Success
404 → Not Found
500 → Server Error
429 → Too Many Requests
API 還可能回 Retry-After,告訴 Client 多久後再嘗試。
可以根據:
IP Address
User ID
API Key
Device
Endpoint
Organization
Subscription Plan
例如:
Free User → 100 requests/hour
Premium User → 10,000 requests/hour
只用 IP 也有缺點:公司或學校的很多 User 可能共用同一 Public IP。因此
Rate Limit Key 的選擇本身也是 Trade-off。
Fixed Window 是把時間切成固定區間,每個區間計算 Request 次數。
Limit = 100 requests/minute
12:00:00 ~ 12:00:59 → Window #1
12:01:00 ~ 12:01:59 → Window #2
每個 Window 有一個 Counter。
Counter 就是記錄次數的數值:
User 123
Count = 47
下一個 Request:
47 → 48
超過 Limit 就 Reject。
假設:
Limit = 100 requests/minute
User 在:
12:00:59 → 100 Requests
12:01:00 → 100 Requests
兩個 Window 都合法,但約 1 秒內可能通過 200 Requests。
這就是:
Boundary Problem
Burst 是短時間突然出現大量 Traffic。
Normal → 100 req/s
Burst → 10,000 req/s
Rate Limiter 的重要目的之一就是控制 Burst 對系統的壓力。
Sliding Window 不只看固定分鐘,而是從「現在」往前看一段時間。
例如現在:
12:01:30
一分鐘 Sliding Window 看:
12:00:30 ~ 12:01:30
而不是只看:
12:01:00 ~ 12:01:59
一種方法是保存每個 Request 的 Timestamp(發生時間點)。
收到新 Request:
1. 移除 Window 外的 Timestamp
2. 計算剩下多少 Requests
3. 低於 Limit → Allow
4. 否則 → Reject
優點:
Accurate
缺點:
Need More Memory
所以又是 Accuracy vs Cost 的 Trade-off。
Token Bucket 可以想像一個裝 Token 的桶子。
每個 Request:
需要 1 Token
有 Token:
Allow
沒有:
Reject / Wait
例如:
Capacity = 10 Tokens
Refill Rate = 2 Tokens/sec
一開始有 10 Tokens,突然來 8 Requests,可以直接通過。之後每秒再補 2
Tokens。
所以 Token Bucket:
允許一定程度的短時間 Burst,但控制長期平均速度。
Leaky Bucket 像一個會漏水的桶子:
Incoming Requests
↓
Bucket
↓
Fixed Output Rate
即使瞬間進來很多 Requests,也用較穩定的速度往外處理。
簡化比較:
Token Bucket
→ Allows some burst
Leaky Bucket
→ Smooths traffic
如果放在 Backend 裡:
User → Backend → Rate Limiter
Request 已經消耗部分 Backend Resource。
因此常希望更早處理:
User
↓
API Gateway
↓
Rate Limiter
↓
Backend
API Gateway 可以理解成:
Client 進入 Backend APIs 前的統一入口。
Client
↓
API Gateway
├── /users → User Service
├── /orders → Order Service
└── /pay → Payment Service
它常處理:
Authentication
Routing
Rate Limiting
Logging
Reverse Proxy 是站在 Server 前面,先接 Client Request,再轉送到後方
Server 的 Component。
Client
↓
Reverse Proxy
↓
Backend
它可能提供 Routing、TLS Termination、Load Balancing、Caching、Rate
Limiting。
如果有:
Backend #1
Backend #2
Backend #3
Limit 是:
100 requests/minute
User 被分散:
Backend #1 → 50
Backend #2 → 50
Backend #3 → 50
每台都認為沒超過 100,但 User 實際送了:
150 Requests
這就是 Distributed Rate Limiting Problem。
多台 Backend 需要 Shared Counter:
Backend #1 ─┐
Backend #2 ─┼→ Redis
Backend #3 ─┘
Redis:
rate:user:123 = 87
所有 Server 看到同一份 Rate Limit State。
假設兩台 Server 同時看到:
Count = 99
如果都做:
GET → +1 → SET
可能產生錯誤。
Atomic Operation 可以理解成:
一個操作在系統看起來像不可拆開的一個完整動作。
Redis 的:
INCR rate:user:123
可以用 Atomic Increment 避免簡單的 Counter Race。
Race Condition:
多個 Request / Thread 同時操作 Shared Data,結果受到執行順序影響。
Count = 99
Server A reads 99
Server B reads 99
A writes 100
B writes 100
理論上應該 101,最後卻可能只有 100。
所以 Distributed Rate Limiter 還要考慮:
Concurrency
Atomicity
Fixed Window 可以概念化成:
Key:
rate:user:123:12:00
Value:
57
並設定 Expiration。
流程:
INCR
↓
Count <= Limit?
├── Yes → Allow
└── No → 429
實作時還要注意 INCR 與 Expiration 等多個操作之間的 Failure / Atomicity
問題。
假設 Rate Limiter 的 Store 掛掉:
Redis ❌
現在要:
全部 Allow?
全部 Reject?
這就是:
Fail Open vs Fail Closed
Rate Limiter Failure 時仍 Allow。
優點:
Availability Higher
缺點:
Backend loses protection
Failure 時 Reject。
優點:
Protect Backend / Security
缺點:
Normal Users may be rejected
沒有永遠正確答案,要看 Business Requirement 與 Risk。
Load Balancer
→ Distribute Traffic
Rate Limiter
→ Control Traffic
Load Balancer 決定 Request 去哪台 Server;Rate Limiter 決定 Request
能不能進來。
Message Queue
→ Accept work and process later
Rate Limiter
→ Reject / delay excessive traffic
例如:
API Abuse → Rate Limiter
Background Traffic Burst → Message Queue
不同 API 成本不同:
GET /products
→ 1000 req/min
POST /login
→ 10 req/min
POST /generate-ai-report
→ 5 req/min
這叫:
Per-Endpoint Rate Limiting
AI API 甚至可以做 Cost-based Limiting:
GET /profile → 1 token
Generate AI Report → 20 tokens
不只計算 Request 數,也考慮 Request Cost。
如果 Login API 遭大量 Password Guessing:
可以考慮:
Per IP Limit
+
Per Account Limit
例如:
Per IP → 20 attempts/min
Per Account → 5 attempts/min
只限制 IP,Attacker 可能換 IP;只限制 Account,也可能攻擊大量不同
Account。
所以可能需要多層 Policy。
如果 10 台 Backend 每台各自限制:
100 req/min
User 經 Load Balancer 分散後,總量可能遠超 100。
因此需要:
Shared State
例如 Redis,再繼續思考:
Atomic Counter
Redis Availability
Network Latency
Fail Open / Closed
如果:
1,000,000 Requests/sec
每個 Request 都要查 Redis,Redis 也可能成為 Bottleneck。
可能進一步考慮:
Redis Cluster
Sharding
Local State
Multiple Rate Limiter Nodes
Approximate Counters
再次證明:
解決一個 Bottleneck,可能創造下一個 Bottleneck。
收到:
429 Too Many Requests
Client 不應立即瘋狂 Retry。
Server 可以提供:
Retry-After
Client 則搭配 Day 11 的:
Exponential Backoff
讓 Retry 更平滑。
User
↓
CDN
↓
API Gateway
↓
Rate Limiter
↓
Load Balancer
↓
Backend
├── Redis
├── Database
└── Message Queue
Rate Limiter 的核心角色:
在昂貴工作發生之前先保護系統。
1. Rate / Request Rate 是什麼?
2. Threshold 是什麼?
3. Rate Limiter 是什麼?
4. HTTP 429 是什麼?
5. Rate Limit 可以根據哪些 Key?
6. IP-based Limiting 有什麼問題?
7. Fixed Window 是什麼?
8. Boundary Problem 是什麼?
9. Burst Traffic 是什麼?
10. Sliding Window 是什麼?
11. Sliding Window Log 的 Trade-off?
12. Token Bucket 是什麼?
13. Token Bucket 為什麼允許 Burst?
14. Leaky Bucket 是什麼?
15. Token Bucket vs Leaky Bucket?
16. API Gateway 是什麼?
17. Reverse Proxy 是什麼?
18. Distributed Rate Limiting 是什麼?
19. 多台 Backend 各自計數為什麼有問題?
20. 為什麼 Redis 適合 Shared Counter?
21. Atomic Operation 是什麼?
22. Race Condition 是什麼?
23. Fail Open 是什麼?
24. Fail Closed 是什麼?
25. Fail Open vs Fail Closed 怎麼選?
26. Rate Limiting vs Load Balancing?
27. Rate Limiting vs Message Queue?
28. Per-Endpoint Rate Limit?
29. Login API 被 Password Guessing 怎麼設計?
30. Rate Limiter 自己成為 Bottleneck 怎麼辦?
31. Retry-After 是什麼?
32. Client 為什麼要使用 Backoff?
Rate Limiter 核心:
Request
↓
Identify Client
↓
Check Rate
↓
Within Limit?
├── Yes → Allow
└── No → Reject / Throttle
常見 Algorithm:
Fixed Window
Sliding Window
Token Bucket
Leaky Bucket
Distributed System 還要考慮:
Shared Counter
Atomicity
Race Condition
Redis Availability
Fail Open / Fail Closed
最重要的一句:
Rate Limiter 的目的不是單純拒絕 User,而是在有限的 System Capacity
下控制 Traffic,保護 Backend、Database 與其他昂貴資源。
現在我們有很多 Distributed Components:
Backend Servers
Cache
Database Replicas
Database Shards
CDN
Message Queue
Rate Limiter
但不同 Server 上的資料:
不一定在同一時間完全相同
例如 Day 8:
Primary → Wei-Hong
Replica → Alvin
Replica 因 Replication Lag 還沒更新。
下一篇:
Day 13|Consistency:為什麼 Distributed System 有時會讀到舊資料?
會先從零解釋:
State
Consistency
Strong Consistency
Eventual Consistency
Stale Read
Read-After-Write Consistency
再用 User Profile、Social Media Like Count、Shopping Cart、Bank Balance
比較不同 Consistency Requirement。