Khi một API bị gọi quá nhiều trong thời gian ngắn, hệ thống có thể chậm, tốn tài nguyên hoặc bị lạm dụng để dò mật khẩu, gửi spam và làm cạn kiệt dịch vụ bên thứ ba. Rate limiting là cơ chế giới hạn số lượng yêu cầu mà một đối tượng được phép thực hiện trong một khoảng thời gian. Đây không chỉ là tính năng bảo mật; nếu thiết kế đúng, nó còn giúp hệ thống ổn định hơn trong giờ cao điểm. ## Bắt đầu từ việc xác định giới hạn phù hợp Không nên áp dụng một con số giống nhau cho mọi endpoint. API đọc dữ liệu công khai có thể cho phép nhiều yêu cầu hơn, trong khi endpoint đăng nhập, gửi mã xác thực hoặc tạo đơn hàng cần giới hạn chặt hơn. Một chính sách thực tế có thể gồm: - Theo địa chỉ IP: phù hợp với người dùng chưa đăng nhập, nhưng cần cẩn thận vì nhiều người có thể dùng chung mạng. - Theo tài khoản hoặc API key: phản ánh đúng danh tính hơn sau khi người dùng đăng nhập. - Theo endpoint và phương thức HTTP: thao tác ghi dữ liệu thường cần giới hạn nghiêm ngặt hơn thao tác đọc. - Theo cấp độ sử dụng: tài khoản miễn phí, trả phí hoặc dịch vụ nội bộ có thể có quota khác nhau. Hãy phân biệt rate limit và quota. Rate limit kiểm soát tốc độ trong một khoảng thời gian ngắn, còn quota thường giới hạn tổng số lượt dùng trong ngày, tháng hoặc một chu kỳ thanh toán. ## Chọn thuật toán đơn giản nhưng phù hợp Fixed window chia thời gian thành các cửa sổ cố định, chẳng hạn mỗi phút. Cách này dễ triển khai nhưng có thể cho phép quá nhiều yêu cầu dồn vào ranh giới giữa hai cửa sổ. Sliding window chính xác hơn nhưng cần lưu nhiều thông tin hơn. Token bucket cho phép tích lũy một số token trong thùng; mỗi yêu cầu tiêu thụ token và token được nạp lại đều đặn. Cách này thường phù hợp khi muốn cho phép các đợt truy cập ngắn nhưng vẫn giữ tốc độ trung bình ổn định. Ở môi trường nhiều máy chủ, không nên lưu bộ đếm chỉ trong bộ nhớ của từng tiến trình. Người dùng có thể gửi các yêu cầu liên tiếp đến những máy chủ khác nhau và vô tình vượt giới hạn. Một kho lưu trữ dùng chung như Redis có thể hỗ trợ bộ đếm với thời gian hết hạn. Các thao tác tăng bộ đếm và kiểm tra giới hạn cần có tính nguyên tử; nếu tách thành hai bước riêng, nhiều yêu cầu đồng thời vẫn có thể vượt qua kiểm tra. ## Phản hồi để client biết cách xử lý Khi vượt giới hạn, server nên trả về mã HTTP 429 Too Many Requests thay vì một lỗi 500 chung chung. Có thể bổ sung các header như giới hạn tối đa, số lượt còn lại và thời điểm thử lại. Nếu biết thời gian chờ, header Retry-After giúp client thực hiện backoff thay vì liên tục gửi lại. Ở phía ứng dụng, không nên retry vô hạn hoặc retry ngay lập tức. Client nên dùng exponential backoff, thêm một khoảng ngẫu nhiên nhỏ để nhiều client không cùng gửi lại tại một thời điểm. Với giao diện người dùng, hãy hiển thị thông báo dễ hiểu, giữ lại dữ liệu biểu mẫu và cho phép thử lại khi phù hợp. Một lần nhấn nút bị từ chối không nên khiến người dùng mất toàn bộ công sức vừa nhập. ## Đừng dùng rate limiting như biện pháp duy nhất Rate limiting không thay thế xác thực, phân quyền, chống giả mạo yêu cầu hay kiểm tra dữ liệu đầu vào. Với endpoint đăng nhập, có thể kết hợp giới hạn theo IP và theo tài khoản, nhưng cần tránh khóa vĩnh viễn tài khoản chỉ vì một mạng dùng chung bị lạm dụng. Với endpoint gửi email hoặc SMS, nên có thêm giới hạn theo người nhận, thiết bị và khoảng thời gian giữa hai lần gửi. ## Kiểm thử và theo dõi sau khi triển khai Hãy kiểm thử các trường hợp vượt giới hạn, nhiều máy chủ cùng cập nhật bộ đếm, thời gian hết hạn và việc đồng hồ hệ thống không đồng nhất. Theo dõi số lượng phản hồi 429 theo endpoint, loại danh tính và phiên bản client. Nếu tỷ lệ tăng đột biến, đó có thể là dấu hiệu tấn công, lỗi vòng lặp retry hoặc một thay đổi giao diện khiến client gửi yêu cầu lặp. Một chính sách tốt không phải là chính sách chặn nhiều nhất, mà là chính sách bảo vệ được tài nguyên trong khi người dùng hợp lệ vẫn hoàn thành công việc. Hãy bắt đầu với giới hạn có lý do rõ ràng, ghi lại cách tính, cung cấp phản hồi nhất quán và điều chỉnh dựa trên dữ liệu vận hành thực tế.