DRL(distributed rate limiter)
Requirements
High Level Design
Block Diagram
Request Flow
Flow Diagram
Rate Limiter System Design(github)
Algorithms
Fixed Window Counter
Multithreaded Code
Divide time into fixed windows (e.g., 00:00–00:59, 01:00–01:59).
Maintain a counter per window. When a request arrives, increment the
counter. If the counter exceeds the limit, reject the request. The
counter resets at the window boundary.
// 10 req/min
Window 1 (12:00 - 12:01) Window 2 (12:01 - 12:02)
|-------10 req-------| |-------10 req--------|
| | | | |
drop
Sliding Window
Maintain the sliding window (let suppose 60 seconds). And fixed tokens
are allowed in this window.
Sliding window counts only requests within the last N seconds by
remembering when each request arrived. It never resets abruptly at a
fixed boundary.
Last 60 seconds at 12:01:05: 80 requests
Sliding Window Counter (Approximate Sliding Window)
Approximate sliding window combines two adjacent fixed windows and weights them to approximate the true moving interval.
Window A (11:00 - 11:01): 60 req
Window B (11:01 - 11:02): 30 req
Weighted count ≈ 15 + 30 = 45 requests
Leaky bucket
Leaky bucket releases requests at a steady pace. Incoming requests may queue briefly, but the output rate stays smooth.
Bucket leaks 1 request/sec
Burst of 10 requests → 1 allowed each second until the burst is drained
Token bucket
Each request consumes one token — if none remain, deny immediately.
That matches how clients and gateways behave.
Runs inside atomic Lua script on Redis.
Bucket capacity 10, refill 1 token/sec
Start with 10 tokens. A burst of 10 is allowed immediately, then 1 more
request per second as tokens refill.
# Token bucket state (per rule + subject key)
bucket:{rule_id}:{subject} → {
tokens: FLOAT,
last_refill: TIMESTAMP
}
function allow_request(bucket, now, rate, capacity):
elapsed = now - bucket.last_refill
refill = elapsed * rate
bucket.tokens = min(capacity, bucket.tokens + refill)
bucket.last_refill = now
if bucket.tokens >= 1:
bucket.tokens -= 1
return allowed, bucket.tokens
else:
return denied, 0
# Example:
# capacity=10, rate=1 token/sec
# start with 10 tokens → 10 requests allowed immediately
# then 1 request allowed per second as tokens refill