Mọi API công khai đều cần giới hạn tần suất — không có nó, một khách hàng viết vòng lặp sai hoặc một kẻ tấn công đơn lẻ có thể làm sập dịch vụ cho tất cả. Nhưng câu tưởng chừng rõ ràng "100 yêu cầu mỗi phút" thực ra có ít nhất bốn cách hiểu, và chúng cho ra hành vi khác hẳn nhau ở những ranh giới quan trọng.
Cửa sổ cố định — đơn giản và có lỗ hổng
Đếm số yêu cầu trong mỗi phút tròn, sang phút mới thì đặt lại về 0. Dễ viết nhất, tốn ít bộ nhớ nhất, nhưng có một lỗ hổng biên nghiêm trọng: người dùng gửi 100 yêu cầu ở giây thứ 59 và 100 yêu cầu nữa ở giây thứ 61 — tức 200 yêu cầu trong hai giây mà vẫn hợp lệ theo luật, vì chúng rơi vào hai phút khác nhau. Kẻ muốn tấn công biết chính xác cách khai thác điều này.
Cửa sổ trượt — chính xác nhưng đắt
Đếm số yêu cầu trong 60 giây vừa qua tính từ đúng thời điểm hiện tại. Không còn lỗ hổng biên, vì cửa sổ luôn di chuyển theo. Cái giá là phải lưu dấu thời gian của từng yêu cầu để biết cái nào đã rơi ra khỏi cửa sổ — tốn bộ nhớ khi lượng yêu cầu lớn. Cách dung hoà phổ biến là chỉ giữ hai bộ đếm (cửa sổ hiện tại và cửa sổ trước) rồi nội suy tuyến tính giữa chúng, đủ chính xác mà rẻ.
Túi token — cho phép bùng nổ có kiểm soát
Hình dung một cái túi chứa tối đa N token, được nạp thêm đều đặn theo thời gian. Mỗi yêu cầu tiêu một token; hết token thì bị từ chối. Điểm hay nằm ở chỗ nó cho phép bùng nổ có kiểm soát: người dùng im lặng một lúc thì túi tích đầy, rồi có thể gửi một loạt nhanh. Với API thật, đây thường chính là hành vi mong muốn — con người và chương trình đều hay gọi theo cụm chứ không rải đều từng giây.
Xô rò — làm phẳng hoàn toàn
Yêu cầu xếp vào một hàng đợi và được lấy ra xử lý với tốc độ cố định, như nước rò qua một cái lỗ nhỏ ở đáy xô. Nó làm phẳng lưu lượng tuyệt đối, phù hợp khi thứ nằm phía sau — một cơ sở dữ liệu cũ, một dịch vụ bên thứ ba mong manh — không chịu được đột biến. Cái giá là độ trễ chờ đợi trong hàng, và yêu cầu có thể bị bỏ nếu hàng đầy.
Chọn thế nào
- API cho lập trình viên — túi token, vì bùng nổ ngắn là hành vi bình thường và hợp lý.
- Bảo vệ một thứ mong manh phía sau — xô rò, để dòng chảy vào luôn phẳng dù đầu vào có giật cục.
- Chống lạm dụng và spam — cửa sổ trượt, vì ở đây bạn cần con số chính xác và không muốn để lọt lỗ hổng biên.
Và dù chọn thuật toán nào, hãy luôn trả về trong header cho biết còn bao nhiêu lượt và khi nào giới hạn đặt lại. Một giới hạn không nói rõ chính là nguồn gốc của những vòng lặp thử-lại mù quáng — khách hàng bị từ chối mà không biết vì sao sẽ thử lại ngay lập tức, làm tình hình tệ hơn đúng lúc dịch vụ đang cần được thở.
Thảo luận