Các thuật toán (fixed/sliding window, token bucket, leaky bucket) đã so sánh ở câu định nghĩa rate limiting — ở đây là các quyết định khi triển khai cho một API công khai đang bị gọi quá nhiều:
- Chọn token bucket cho traffic chung: client hợp lệ hay có burst ngắn (mở trang gọi 5–7 request) — bucket cho tiêu dồn tới trần nhưng vẫn ghìm tốc độ trung bình. Endpoint nhạy cảm (login, OTP, gửi mail) thì siết bằng cửa sổ đếm chặt, trần thấp riêng.
- Key theo gì: đã auth → per-API-key/per-user (công bằng, không phạt oan văn phòng/4G chung IP); chưa auth → per-IP nhưng nới trần vì NAT; login → key kép account + IP để chống cả đổi IP lẫn spray nhiều account.
- Đặt ở đâu: tại API gateway / reverse proxy / edge — chặn trước khi request tốn tài nguyên app. Counter bắt buộc ở store chia sẻ (Redis): đếm cục bộ từng instance thì trần thực tế = limit × số instance.
- Hợp đồng trả về:
429 Too Many Requests+Retry-After(số giây chờ) +RateLimit-*báo quota còn lại — thiếuRetry-Afterthì client retry dồn dập đúng lúc hệ đang quá tải.
js
// fixed-window counter in Redis: INCR + EXPIRE per key + window
const k = "rl:" + apiKey + ":" + Math.floor(Date.now() / 60000) // 1-min bucket
const n = await redis.incr(k)
if (n === 1) await redis.expire(k, 60)
if (n > 100) {
res.setHeader("Retry-After", "60")
return res.sendStatus(429)
}Edge case: INCR + EXPIRE là hai lệnh — chết giữa chừng để lại key không TTL → gói vào Lua script cho atomic. Đừng cho health check và webhook của đối tác chung bucket với user thường — kẻo tự chặn chính mình.
Lỗi hay gặp: giới hạn theo IP khi user sau NAT; đếm cục bộ từng instance; quên Retry-After.