Thiết kế Rate Limiter — chuyện không chỉ đơn giản là đếm
Chuyện cái API bị quá tải
Hồi mới ra trường, mình từng deploy một cái API public. Mọi chuyện êm đẹp cho tới một ngày — tự nhiên server ngỏm, database chết, log toàn "too many connections". Hoá ra có một bạn dev khác quên set delay trong vòng lặp gọi API mình, 10K request trong 2 giây. Lúc đó mình mới biết: rate limiter không phải là "nice to have", mà là must-have.
Ảnh: Kevin Ku — Pexels
Token Bucket — thuật toán đơn giản nhất
Cơ chế thì dễ hiểu: bạn có cái bucket chứa token, mỗi request phải lấy một token mới được vô. Một tiến trình background bỏ token vô bucket đều đặn (ví dụ 10 token/giây). Hết token thì request bị từ chối, trả về HTTP 429. Dễ code, dễ giải thích — mình nghĩ đây là "Hello World" của rate limiting.
Có một biến thể là Leaky Bucket: thay vì xin token, request vô hàng đợi và được xử lý với tốc độ cố định. Giống như cái phễu — nước đổ vô nhiều cỡ nào cũng chỉ chảy ra một lượng nhất định.
Sliding Window — chính xác hơn
Vấn đề của Token Bucket là nó cho phép burst — bạn có thể tích luỹ token rồi xài một lúc, gây áp lực lên server. Sliding Window giải quyết chuyện này bằng cách nhìn vào khoảng thời gian thực tế (ví dụ 1 phút gần nhất) và đếm số request trong đó. Chính xác hơn, nhưng tốn bộ nhớ hơn.
Ảnh: ThisIsEngineering — Pexels
Distributed rate limiter với Redis
Khi bạn có nhiều server, chuyện bắt đầu rắc rối. User A gửi 6 request — 3 cái vô server 1, 3 cái vô server 2, mỗi server chỉ thấy 3, không cái nào chặn. User A pass được giới hạn 5 request/phút. Giải pháp là dùng Redis làm centralized counter.
Cách implement mình thích: dùng Sorted Set trong Redis. Mỗi user là một key, timestamp của request là score. ZREMRANGEBYSCORE để xoá request cũ, ZCARD để đếm. O(log N) — nhanh, chính xác, không cần lock.
Lời kết
Rate limiter là một trong những thứ "dễ làm khó đúng" nhất trong thiết kế hệ thống. Nếu bạn mới bắt đầu, cứ implement Token Bucket trước — nó giải quyết 80% vấn đề. Khi nào hệ thống lớn dần thì tính chuyện nâng cấp lên distributed với Redis.
Còn bạn, bạn đã bao giờ implement rate limiter cho hệ thống của mình chưa? Dùng thuật toán nào? Share với mình dưới comment nha!