In cloud microservices and public API gateways, Rate Limiting controls the rate of incoming request traffic to protect backend services from resource exhaustion, cascading failures, brute-force credential stuffing, and Distributed Denial of Service (DDoS) attacks.
Implementing effective rate limiters requires selecting the right underlying mathematical algorithm based on traffic burst tolerance, memory constraints, and distributed race conditions across multi-node API gateways.
1. Comparative Analysis of Rate Limiting Algorithms
| Algorithm | Burst Behavior | Memory Footprint | Pros & Cons |
|---|---|---|---|
| Token Bucket | Allows controlled bursts up to bucket capacity $B$. | Minimal (2 integers: token count + last refilled timestamp). | Industry Standard (AWS, Stripe): Highly memory efficient and accommodates short legitimate traffic spikes. |
| Leaky Bucket | Smooths bursts into a constant, fixed output rate. | FIFO Queue memory proportional to queue depth. | Ideal for traffic shaping into downstream systems that require smooth, non-bursty processing rates. |
| Fixed Window Counter | Disallows bursts beyond limit per fixed clock window (e.g. 100 req/min). | Ultra minimal (Single integer counter per window). | Boundary Spike Vulnerability: Up to $2\times$ the rate limit can pass through near the boundary edge of adjacent windows. |
| Sliding Window Log | Zero boundary spikes; precise historical log. | High (Stores log of timestamps for every request in Redis Sorted Set). | Perfect accuracy, but memory consumption grows linearly with request volume. |
| Sliding Window Counter | Smooth approximation combining previous & current window counts. | Extremely low (2 counters). | Overcomes boundary spikes with <1% accuracy error; memory efficient. |
2. Token Bucket Mathematical Formulation
The Token Bucket algorithm refills a bucket with tokens at a constant rate of $r$ tokens per second, up to a maximum capacity $B$. When a request arrives requiring $c$ tokens:
- Calculates tokens added since last request: $\Delta \text{tokens} = (t_{\text{now}} - t_{\text{last}}) \times r$.
- New token count: $\text{tokens}_{\text{current}} = \min(B, \text{tokens}_{\text{old}} + \Delta \text{tokens})$.
- If $\text{tokens}_{\text{current}} \ge c$, consume $c$ tokens and allow the request. Otherwise, return
HTTP 429 Too Many Requests.
3. Atomic Distributed Rate Limiting via Redis Lua Scripts
In a multi-node API Gateway cluster (e.g. 10 Kong or Nginx instances), querying Redis with separate GET and SET commands introduces race conditions where two concurrent gateway requests over-read token counts.
By executing rate limit evaluations inside an atomic Redis Lua Script, Redis executes the entire logic sequentially in a single single-threaded step:
4. IETF Standard HTTP Rate Limit Response Headers
When serving rate-limited responses, APIs should populate standardized HTTP response headers so client SDKs can implement intelligent exponential backoff retry loops:
5. Architectural Best Practices
- Layered Defense: Enforce coarse rate limits at the CDN/Edge (Cloudflare / AWS WAF) to block volumetric DDoS spikes, and fine-grained user/tenant limits at the API Gateway layer.
- Graceful Degradation: If the central Redis cluster suffers an outage, configure API gateways to fallback to local in-memory token buckets (fail-open) to keep applications accessible.
Join the Technical Discussion
Have questions about this architecture? Drop a comment below.