Explore all courses in this category.
Explore all courses in this category.
Nguyễn Đình Cường
0 comment
09 Nov, 2026
Một API công khai mà không giới hạn tần suất giống cửa hàng mở toang không bảo vệ: một script lỗi hoặc một kẻ xấu có thể làm sập dịch vụ của bạn chỉ trong vài phút. Rate limiting đặt trần số lần gọi để bảo vệ hệ thống và đảm bảo công bằng giữa người dùng.
Bạn chọn "khóa" để đếm: theo địa chỉ IP (cho người chưa đăng nhập), theo id người dùng hoặc API key (cho người đã xác thực), hoặc theo endpoint (endpoint đăng nhập nên chặt hơn endpoint đọc). Lưu ý nhiều người có thể dùng chung một IP (mạng công ty, NAT của nhà mạng), nên giới hạn theo IP cần thoáng hơn.
Chia thời gian thành các khung cố định (ví dụ mỗi phút) và đếm số request trong khung hiện tại. Với Redis:
import time, redisr = redis.Redis()def allow(key, limit=60, window=60): bucket = f"rl:{key}:{int(time.time() // window)}" count = r.incr(bucket) if count == 1: r.expire(bucket, window) return count <= limitNhược điểm: hiện tượng "bùng nổ ở biên khung". Người dùng có thể gửi 60 request ở giây cuối của phút này và 60 request ở giây đầu của phút sau, tức 120 request trong vài giây.
Đếm số request trong khoảng thời gian trượt (ví dụ 60 giây gần nhất) thay vì khung cố định, mượt hơn và chính xác hơn. Cách cài đặt phổ biến là dùng sorted set của Redis, lưu mỗi request với điểm là mốc thời gian:
def allow_sliding(key, limit=60, window=60): now = time.time() k = f"rl:{key}" pipe = r.pipeline() pipe.zremrangebyscore(k, 0, now - window) # bỏ request quá cũ pipe.zadd(k, {str(now): now}) pipe.zcard(k) pipe.expire(k, window) _, _, count, _ = pipe.execute() return count <= limitChính xác hơn nhưng tốn bộ nhớ hơn với giới hạn lớn, vì lưu từng request.
Hình dung một cái xô chứa tối đa N token. Mỗi request lấy một token; token được bổ sung đều đặn theo tốc độ cố định. Khi xô rỗng, request bị từ chối. Mẫu này cho phép đột biến ngắn (dùng hết token tích lũy) nhưng vẫn giữ tốc độ trung bình dài hạn, nên rất tự nhiên với lưu lượng thực tế.
class TokenBucket: def __init__(self, capacity, refill_per_sec): self.capacity = capacity self.tokens = capacity self.rate = refill_per_sec self.last = time.time() def allow(self): now = time.time() self.tokens = min(self.capacity, self.tokens + (now - self.last) * self.rate) self.last = now if self.tokens >= 1: self.tokens -= 1 return True return FalseVí dụ trên chạy trong bộ nhớ một tiến trình. Với nhiều server, trạng thái phải nằm ở nơi dùng chung như Redis và thao tác kiểm tra cần nguyên tử (thường dùng script Lua) để tránh đua dữ liệu.
Trả về mã 429 Too Many Requests và cho client biết tình hình qua header:
HTTP/1.1 429 Too Many RequestsRetry-After: 30X-RateLimit-Limit: 60X-RateLimit-Remaining: 0Một số API dùng header RateLimit-* theo chuẩn đề xuất mới. Điều quan trọng là client biết khi nào được thử lại. Phía client cũng nên tôn trọng và dùng backoff khi nhận 429.
throttle:60,1 hoặc định nghĩa RateLimiter::for().express-rate-limit.limit_req_zone giới hạn ngay ở lớp proxy, trước khi tới ứng dụng.// Laravel: giới hạn đăng nhập chặt hơnRateLimiter::for('login', function (Request $request) { return Limit::perMinute(5)->by($request->ip() . '|' . $request->input('email'));});Khi ứng dụng đứng sau proxy hoặc CDN, địa chỉ IP bạn thấy có thể là của proxy chứ không phải của người dùng thật. Hãy cấu hình danh sách proxy tin cậy để đọc đúng IP từ header như X-Forwarded-For, nếu không toàn bộ người dùng sẽ bị tính chung một bộ đếm và bị chặn hàng loạt. Ngược lại, đừng tin mù quáng header này nếu nó có thể bị giả mạo bởi chính client. Hãy ghi log mỗi khi giới hạn bị kích hoạt để phân biệt lưu lượng bất thường với người dùng thật gặp bất tiện, và điều chỉnh ngưỡng dựa trên dữ liệu chứ không phải phỏng đoán.
Hãy bắt đầu với giới hạn đơn giản ở lớp proxy hoặc middleware, đặt chặt hơn cho các endpoint nhạy cảm như đăng nhập, đặt lại mật khẩu, gửi OTP. Khi nhu cầu lớn dần, chuyển sang token bucket hoặc sliding window dùng Redis. Rate limiting không phải thứ xa xỉ, mà là phần cơ bản của một API sẵn sàng cho thế giới thật.
Khi cần một lộ trình học có hệ thống, bạn có thể tham khảo khóa học trên codebem.com.
Nguyễn Đình Cường
0 comment