Tổng quan về API Rate Limiting
API rate limiting là cơ chế giới hạn số lượng request mà client được phép gửi đến server trong một khoảng thời gian xác định. Mục tiêu chính của việc áp dụng rate-limit là:
- Bảo vệ hệ thống khỏi các cuộc tấn công DDoS hoặc spike traffic đột ngột.
- Đảm bảo trải nghiệm công bằng cho tất cả client, tránh tình trạng “hành hung” tài nguyên bởi một số user quá “nhiệt tình”.
- Kiểm soát chi phí khi sử dụng dịch vụ bên thứ ba (như cloud API, database managed service) tính phí theo số request.
Định nghĩa rate-limit và throttling
- Rate-limit: Giới hạn tần suất request (ví dụ 100 request/phút).
- Throttling: Hành động từ chối hoặc làm chậm bớt các request vượt ngưỡng, thường đi kèm trả về HTTP 429.
Khi nào cần áp dụng rate-limit
- Bảo mật: Ngăn bot hoặc attacker quét API quá nhanh.
- Ổn định: Tránh tình trạng server quá tải, làm chậm hoặc crash.
- Công bằng: Cân bằng tài nguyên giữa các user, đặc biệt với tier miễn phí vs trả phí.
So sánh “soft limit” vs “hard limit”
- Soft limit: Khi vượt ngưỡng, client chỉ bị cảnh báo hoặc throttled giảm tốc.
- Hard limit: Request ngay lập tức bị từ chối (HTTP 429) khi vượt ngưỡng.
2. Các kiểu Rate-Limit phổ biến
Mỗi loại cơ chế có ưu nhược điểm riêng, phù hợp với các tình huống khác nhau.

Fixed window (cửa sổ cố định)
- Đếm số request trong “cửa sổ” cố định (ví dụ mỗi phút).
- Ưu: Đơn giản, dễ implement.
- Nhược: Gặp vấn đề “thời điểm chéo ranh giới” (ví dụ 1 request cuối phút trước + 100 request đầu phút sau vẫn vượt mức trong 60s).
Sliding window (cửa sổ trượt)
- Lưu lại timestamps của request, tính số request trong khoảng trượt gần nhất.
- Ưu: Công bằng hơn, giải quyết edge case fixed window.
- Nhược: Phức tạp về bộ nhớ và thao tác tính toán.
Token bucket và Leaky bucket

- Token bucket: Server cấp token với tốc độ cố định; mỗi request “tiêu” token. Nếu hết token, request bị chặn.
- Leaky bucket: Request vào “xô” chảy với tốc độ cố định; quá tải thì xô đầy và bỏ bớt.
- Ví dụ đơn giản (JavaScript):
class TokenBucket {
constructor(capacity, refillRate) {
this.capacity = capacity;
this.tokens = capacity;
this.refillRate = refillRate; // tokens per ms
this.last = Date.now();
}
refill() {
const now = Date.now();
this.tokens = Math.min(this.capacity, this.tokens + (now - this.last) * this.refillRate);
this.last = now;
}
remove(count = 1) {
this.refill();
if (this.tokens >= count) {
this.tokens -= count;
return true;
}
return false;
}
}
// Sử dụng:
const bucket = new TokenBucket(100, 0.001); // 100 token, 1 token mỗi giây
if (!bucket.remove()) {
// trả về 429
}
Rate-limit theo user, theo IP, theo API key
- Theo API key: Phổ biến với public API.
- Theo IP: Dùng cho các service không yêu cầu đăng nhập.
- Theo user/account: Cho API nội bộ, cần nhận diện user.
3. Xác định nhu cầu và tải dự kiến
Trước khi đặt giới hạn, phải hiểu rõ traffic thực tế và yêu cầu của ứng dụng.
Thu thập số liệu traffic (metrics, logs)

- Dùng Prometheus, Grafana, hoặc ELK stack (Elasticsearch + Logstash + Kibana) để lưu và query dữ liệu request.
- Ví dụ query Prometheus để đếm request mỗi phút:
sum(rate(http_requests_total[1m]))
Phân tích patterns (peak vs normal)
- Xác định peak hour, peak day dựa trên dữ liệu lịch sử.
- So sánh peak vs baseline để thấy biên độ tăng giảm.
Tính toán throughput tối đa & tối thiểu
- Throughput tối thiểu = average requests per second bình thường × hệ số an toàn (1.2–1.5).
- Throughput tối đa = peak requests per second (dùng để cấu hình burst allowance).
4. Thiết lập ngưỡng Rate-Limit “đủ”
Để không đặt giới hạn quá chặt khiến user bị dồn request thất bại, cũng không quá lỏng lẻo để hệ thống quá tải, bạn cần tuân theo các nguyên tắc sau:
Công thức cơ bản
Bạn có thể bắt đầu với công thức đơn giản:
R = μ × S
- R: số request tối đa mỗi window (ví dụ mỗi phút)
- μ: throughput bình quân (requests/giây) thu thập được
- S: độ dài window tính bằng giây (ví dụ 60 giây)
Ví dụ: nếu hệ thống xử lý ổn định trung bình 5 rps, window 60s → R = 5 × 60 = 300 requests/phút.
Điều chỉnh theo user-tier
Phân cấp giới hạn dựa trên gói dịch vụ:
- Free: 100 requests/phút
- Basic: 300 requests/phút
- Premium: 1000 requests/phút
Áp dụng thông số từ công thức cơ bản làm chuẩn, sau đó nhân hệ số ưu tiên (ví dụ free ×0.33, basic ×1, premium ×3).
Thêm burst allowance
Cho phép spike ngắn với “burst tokens” để tránh thất bại khi request dồn:
- Đặt bucket capacity lớn hơn R, ví dụ capacity = 1.5 × R
- Refill rate = R / S
Ví dụ với R = 300/phút:
const capacity = 300 * 1.5; // 450 token
const refillRate = 300 / 60; // 5 token/giây
const bucket = new TokenBucket(capacity, refillRate);
5. Giám sát và tự động điều chỉnh
Theo dõi liên tục và tự động điều chỉnh giúp rate-limit luôn phù hợp với biến động traffic.
Sử dụng Prometheus/Grafana để alert
- Tạo metric
rate_limit_exceeded_totalđếm số lần trả về 429. - Rule alert khi
rate_limit_exceeded_totaltrên ngưỡng, ví dụ > 100 trong 5 phút. - Dashboard hiển thị:
- Requests tổng
- Requests bị giới hạn
- Tỷ lệ giới hạn (%)
Auto-scaling vs auto-throttling
- Auto-scaling: Tăng thêm instance khi queue dài hoặc CPU cao.
- Auto-throttling: Tự động giảm R khi hệ thống căng thẳng, dựa trên metric như CPU > 80% hoặc latency > SLA.
Feedback loop: log và adaptive rate-limit
- Lưu log chi tiết: timestamp, user, route, result (allowed/denied).
- Chạy job hàng ngày phân tích: nếu tỷ lệ 429 > 5% → giảm R 10%; nếu < 1% → tăng R 10%.
6. Xử lý khi client vượt quá giới hạn
Đảm bảo UX tốt khi client gặp giới hạn request.
HTTP status code (429 Too Many Requests)
Luôn trả về 429 và không dùng 503 hay 400 để client dễ nhận biết.
Thông điệp Retry-After
Gửi header Retry-After (giây hoặc timestamp) để client biết khi nào thử lại:
HTTP/1.1 429 Too Many Requests
Retry-After: 60
Content-Type: application/json
{
"error": "Rate limit exceeded",
"retry_after": 60
}
Thiết kế retry strategy ở client
- Exponential backoff: delay doubling sau mỗi lần thất bại, kèm jitter ngẫu nhiên.
- Circuit breaker: tạm dừng gọi API nếu liên tiếp thất bại quá 5 lần, reset sau khoảng nghỉ.
7. Case study & ví dụ thực tế
Triển khai rate-limit với NGINX / Envoy
NGINX
http {
limit_req_zone $binary_remote_addr zone=one:10m rate=5r/s;
server {
location /api/ {
limit_req zone=one burst=10 nodelay;
proxy_pass http://backend;
}
}
}
rate=5r/s cho phép 5 request/giây
burst=10 cấp thêm 10 request đột biến
nodelay nghĩa là request burst sẽ không bị delay nếu còn token
Envoy
http_filters:
- name: envoy.filters.http.local_ratelimit
typed_config:
"@type": type.googleapis.com/envoy.extensions.filters.http.local_ratelimit.v3.LocalRateLimit
stat_prefix: http_local_rate_limiter
token_bucket:
max_tokens: 100
tokens_per_fill: 10
fill_interval: 1s
filter_enabled:
runtime_key: local_rate_limit_enabled
default_value: { numerator: 100, denominator: HUNDRED }
response_headers_to_add:
- append_action: OVERWRITE
header:
key: Retry-After
value: "1"
Sử dụng token bucket với max_tokens=100 và refill 10 token mỗi giây
Thêm header Retry-After cho client biết thời gian chờ

Ví dụ code Node.js (Express + express-rate-limit)
const express = require("express");
const rateLimit = require("express-rate-limit");
const app = express();
const limiter = rateLimit({
windowMs: 60 * 1000, // 1 minute
max: 300, // limit each IP to 300 requests per window
message: {
error: "Rate limit exceeded",
retry_after: 60
},
headers: true // gửi Retry-After header
});
app.use("/api/", limiter);
app.get("/api/data", (req, res) => {
res.json({ data: "some data" });
});
app.listen(3000);
- Kết quả trước khi thêm limiter: thường xuyên xảy ra spike và tăng latency
- Kết quả sau: ổn định throughput quanh 5 rps, latency giảm trung bình 30%
So sánh kết quả trước và sau khi tối ưu
- Without rate-limit
- Peak latency: 500ms
- Error rate: 0 nhưng hệ thống dễ quá tải
- With rate-limit
- Peak latency: 200ms
- Error rate: 2% (HTTP 429) trong peak hour
- Hệ thống ổn định, dễ scale ngang
8. Kết luận và best practices
Nhắc lại nguyên tắc “đủ mà vững”
- Xác định nhu cầu thật sự qua metrics trước khi đặt limit
- Cân bằng giữa bảo vệ hệ thống và trải nghiệm user
- Thêm burst allowance để xử lý spike ngắn
Checklist trước khi deploy
- Thu thập đủ dữ liệu traffic (baseline, peak)
- Chọn loại cơ chế rate-limit phù hợp
- Cấu hình rate, burst và tiers rõ ràng
- Thiết lập alert cho metric rate_limit_exceeded
- Kiểm thử bằng load test (locust, k6)
- Triển khai staging trước khi vào production
Tài liệu tham khảo & link thư viện liên quan
- express-rate-limit (npm): https://www.npmjs.com/package/express-rate-limit
- NGINX rate limiting: https://nginx.org/en/docs/http/ngx_http_limit_req_module.html
- Envoy local rate limit filter: https://www.envoyproxy.io/docs/envoy/latest/configuration/http/http_filters/local_rate_limit_filter
- Article “API Rate Limiting Algorithms”: https://www.cloudflare.com/learning/ddos/glossary/rate-limiting
FAQ
Rate-limit khác throttle thế nào?
Rate-limit là giới hạn tần suất request tối đa trong window cố định, còn throttling là hành động làm chậm hoặc từ chối request khi vượt ngưỡng đó.
Nên dùng token bucket hay fixed window?
Token bucket phù hợp khi cần hỗ trợ burst tốt, còn fixed window đơn giản, dễ cài đặt nhưng có edge case tại ranh giới giữa các window.
Làm sao tính burst allowance?
Burst allowance thường lấy 1.2–1.5 lần R (requests per window) để cho phép spike ngắn, sau đó refill về mức R.
Có nên tự động điều chỉnh limit?
Có. Dùng feedback loop (daily job) để tăng/giảm R dựa trên tỉ lệ 429, kết hợp alert để devs kịp thời điều chỉnh.
Cách xử lý retry phía client hiệu quả?
Dùng exponential backoff kèm jitter để tránh thundering herd, và circuit breaker để tạm dừng khi liên tiếp thất bại.








