Tóm tắt
Triển khai giới hạn tốc độ API bằng cách sử dụng thuật toán xô chứa mã thông báo (token bucket) hoặc cửa sổ trượt (sliding window). Trả về các tiêu đề giới hạn tốc độ IETF tiêu chuẩn (RateLimit-Limit, RateLimit-Remaining, RateLimit-Reset) và mã trạng thái 429 Too Many Requests khi vượt quá giới hạn. Modern PetstoreAPI triển khai giới hạn tốc độ với hạn ngạch theo từng người dùng và các phản hồi lỗi rõ ràng.
Giới thiệu
Một client gửi 10.000 yêu cầu đến API của bạn trong một phút. Cơ sở dữ liệu của bạn gặp sự cố. Cảnh báo giám sát của bạn được kích hoạt. Các khách hàng khác của bạn không thể truy cập API. Bạn đang bị tấn công—hoặc có thể chỉ đang đối phó với một client bị lỗi trong vòng lặp thử lại.
Giới hạn tốc độ yêu cầu (Rate limiting) ngăn chặn điều này. Nó giới hạn số lượng yêu cầu mà một client có thể thực hiện trong một khoảng thời gian. Khi họ vượt quá giới hạn, bạn trả về mã trạng thái 429 Too Many Requests. Client sẽ tạm dừng, và API của bạn vẫn hoạt động ổn định.
Swagger Petstore cũ không triển khai giới hạn tốc độ yêu cầu nào. Modern PetstoreAPI triển khai giới hạn tốc độ yêu cầu với các tiêu đề IETF tiêu chuẩn, hạn ngạch theo từng người dùng và các phản hồi lỗi rõ ràng.
Trong hướng dẫn này, bạn sẽ tìm hiểu các thuật toán giới hạn tốc độ yêu cầu, các tiêu đề tiêu chuẩn và cách Modern PetstoreAPI triển khai giới hạn tốc độ yêu cầu một cách chính xác.
Tại sao API cần giới hạn tốc độ yêu cầu (Rate Limiting)
Giới hạn tốc độ yêu cầu bảo vệ API của bạn khỏi bị lạm dụng và đảm bảo việc sử dụng công bằng.
Bảo vệ khỏi lạm dụng
1. Các cuộc tấn công từ chối dịch vụ (DoS)
Kẻ tấn công làm tràn ngập API của bạn bằng các yêu cầu để làm cho nó không khả dụng. Giới hạn tốc độ yêu cầu hạn chế tác động của chúng.
2. Nhồi nhét thông tin xác thực (Credential stuffing)
Kẻ tấn công thử hàng nghìn sự kết hợp tên người dùng/mật khẩu. Giới hạn tốc độ yêu cầu làm chậm chúng lại.
3. Cào dữ liệu (Data scraping)
Bot cào toàn bộ tập dữ liệu của bạn. Giới hạn tốc độ yêu cầu làm cho việc cào dữ liệu không thực tế.
4. Kiểm soát chi phí
Nếu API của bạn gọi các dịch vụ đắt tiền (mô hình AI, API bên thứ ba), giới hạn tốc độ yêu cầu ngăn chặn chi phí vượt quá tầm kiểm soát.
Sử dụng công bằng
1. Ngăn chặn một client độc chiếm tài nguyên
Nếu không có giới hạn tốc độ yêu cầu, một client gửi 1000 yêu cầu/giây có thể làm nghẽn các client khác.
2. Hiệu suất dự đoán được
Giới hạn tốc độ yêu cầu đảm bảo thời gian phản hồi nhất quán cho tất cả các client.
3. Truy cập theo cấp độ
Gói miễn phí: 100 yêu cầu/giờ. Gói trả phí: 10.000 yêu cầu/giờ. Giới hạn tốc độ yêu cầu thực thi các cấp độ này.
Lợi ích vận hành
1. Lập kế hoạch dung lượng
Bạn biết tải tối đa mà API của bạn sẽ xử lý.
2. Khả năng dự đoán chi phí
Giới hạn tốc độ yêu cầu giới hạn chi phí cơ sở hạ tầng.
3. Giảm tải một cách linh hoạt (Graceful degradation)
Khi gặp tải cao, giới hạn tốc độ yêu cầu ngăn chặn các lỗi dây chuyền.
Các thuật toán giới hạn tốc độ yêu cầu
Các thuật toán khác nhau có những đánh đổi khác nhau.
1. Cửa sổ cố định (Fixed Window)
Đếm số lượng yêu cầu trong các khoảng thời gian cố định.
Cách hoạt động:
Cửa sổ 1 (00:00-00:59): 100 yêu cầu được phép
Cửa sổ 2 (01:00-01:59): 100 yêu cầu được phép
Triển khai:
def is_allowed(user_id):
current_minute = get_current_minute()
key = f"rate_limit:{user_id}:{current_minute}"
count = redis.incr(key)
redis.expire(key, 60)
return count <= 100
Ưu điểm:
- Dễ triển khai
- Sử dụng ít bộ nhớ
Nhược điểm:
- Vấn đề về bùng nổ (burst problem): Client có thể thực hiện 100 yêu cầu lúc 00:59 và 100 yêu cầu lúc 01:00 (200 yêu cầu trong 2 giây)
2. Cửa sổ trượt (Sliding Window)
Đếm số lượng yêu cầu trong một cửa sổ thời gian cuộn.
Cách hoạt động:
Lúc 01:30, đếm số lượng yêu cầu từ 00:30 đến 01:30 (trong 60 phút gần nhất).
Triển khai:
def is_allowed(user_id):
now = time.time()
window_start = now - 3600 # 1 giờ trước
key = f"rate_limit:{user_id}"
# Xóa các yêu cầu cũ
redis.zremrangebyscore(key, 0, window_start)
# Đếm số lượng yêu cầu trong cửa sổ
count = redis.zcard(key)
if count < 100:
redis.zadd(key, {now: now})
redis.expire(key, 3600)
return True
return False
Ưu điểm:
- Không có vấn đề về bùng nổ
- Giới hạn tốc độ yêu cầu chính xác
Nhược điểm:
- Sử dụng bộ nhớ cao hơn (lưu trữ dấu thời gian cho mỗi yêu cầu)
- Phức tạp hơn
3. Xô chứa mã thông báo (Token Bucket)
Các mã thông báo được thêm vào một "xô" với tốc độ cố định. Mỗi yêu cầu tiêu thụ một mã thông báo.
Cách hoạt động:
Dung lượng xô: 100 mã thông báo
Tốc độ nạp lại: 10 mã thông báo/giây
Yêu cầu: tiêu thụ 1 mã thông báo
Triển khai:
def is_allowed(user_id):
now = time.time()
key = f"rate_limit:{user_id}"
# Lấy trạng thái hiện tại
data = redis.hgetall(key)
tokens = float(data.get('tokens', 100))
last_refill = float(data.get('last_refill', now))
# Nạp lại mã thông báo
elapsed = now - last_refill
tokens = min(100, tokens + elapsed * 10) # 10 mã thông báo/giây
if tokens >= 1:
tokens -= 1
redis.hset(key, 'tokens', tokens)
redis.hset(key, 'last_refill', now)
redis.expire(key, 3600)
return True
return False
Ưu điểm:
- Cho phép các đợt bùng nổ (tối đa dung lượng xô)
- Giới hạn tốc độ yêu cầu mượt mà
- Tiêu chuẩn công nghiệp
Nhược điểm:
- Phức tạp hơn cửa sổ cố định
- Yêu cầu lưu trữ trạng thái
4. Xô rò rỉ (Leaky Bucket)
Các yêu cầu được thêm vào một hàng đợi và được xử lý với tốc độ cố định.
Cách hoạt động:
Dung lượng hàng đợi: 100 yêu cầu
Tốc độ xử lý: 10 yêu cầu/giây
Ưu điểm:
- Tốc độ đầu ra mượt mà
- Tốt để bảo vệ các dịch vụ hạ nguồn
Nhược điểm:
- Tăng độ trễ (yêu cầu chờ trong hàng đợi)
- Phức tạp để triển khai
Nên sử dụng thuật toán nào?
Đối với hầu hết các API: Xô chứa mã thông báo (Token Bucket)
Đây là tiêu chuẩn công nghiệp, cho phép các đợt bùng nổ hợp lý và cung cấp giới hạn tốc độ yêu cầu mượt mà.
Modern PetstoreAPI sử dụng xô chứa mã thông báo với hạn ngạch theo từng người dùng.
Tiêu đề giới hạn tốc độ yêu cầu tiêu chuẩn
Sử dụng các tiêu đề IETF tiêu chuẩn (draft-ietf-httpapi-ratelimit-headers).
Tiêu đề tiêu chuẩn
RateLimit-Limit: Số lượng yêu cầu tối đa được phép trong khoảng thời gian
RateLimit-Limit: 100
RateLimit-Remaining: Số lượng yêu cầu còn lại trong khoảng thời gian hiện tại
RateLimit-Remaining: 45
RateLimit-Reset: Số giây cho đến khi giới hạn tốc độ được đặt lại
RateLimit-Reset: 3600
Ví dụ về phản hồi
GET /pets
200 OK
RateLimit-Limit: 100
RateLimit-Remaining: 99
RateLimit-Reset: 3600
{
"data": [...]
}
Tiêu đề cũ (Không còn được dùng)
Nhiều API sử dụng các tiêu đề không chuẩn:
X-RateLimit-Limit: 100
X-RateLimit-Remaining: 99
X-RateLimit-Reset: 1710331200
Đừng sử dụng những cái này. Tiền tố X- đã bị loại bỏ, và định dạng không được chuẩn hóa.
Cách Modern PetstoreAPI triển khai giới hạn tốc độ yêu cầu
Modern PetstoreAPI triển khai giới hạn tốc độ yêu cầu theo thuật toán xô chứa mã thông báo với các tiêu đề tiêu chuẩn.
Giới hạn tốc độ yêu cầu theo cấp độ
Gói miễn phí:
- 100 yêu cầu/giờ
- 1.000 yêu cầu/ngày
Gói chuyên nghiệp:
- 10.000 yêu cầu/giờ
- 100.000 yêu cầu/ngày
Gói doanh nghiệp:
- Giới hạn tùy chỉnh
Triển khai
Yêu cầu thành công:
GET /v1/pets
200 OK
RateLimit-Limit: 100
RateLimit-Remaining: 99
RateLimit-Reset: 3540
{
"data": [...]
}
Vượt quá giới hạn tốc độ yêu cầu:
GET /v1/pets
429 Too Many Requests
Content-Type: application/problem+json
RateLimit-Limit: 100
RateLimit-Remaining: 0
RateLimit-Reset: 120
Retry-After: 120
{
"type": "https://petstoreapi.com/errors/rate-limit-exceeded",
"title": "Vượt quá giới hạn tốc độ yêu cầu",
"status": 429,
"detail": "Bạn đã vượt quá giới hạn tốc độ 100 yêu cầu mỗi giờ",
"instance": "/v1/pets",
"retryAfter": 120,
"limit": 100,
"window": "1h"
}
Theo người dùng so với Theo IP
Theo người dùng (yêu cầu đã xác thực):
Giới hạn tốc độ yêu cầu theo ID người dùng hoặc khóa API. Chính xác và công bằng hơn.
user_id = get_authenticated_user()
is_allowed(user_id)
Theo IP (yêu cầu chưa xác thực):
Giới hạn tốc độ yêu cầu theo địa chỉ IP. Kém chính xác hơn (IP dùng chung, VPN) nhưng vẫn tốt hơn là không có gì.
ip_address = request.remote_addr
is_allowed(ip_address)
Modern PetstoreAPI sử dụng giới hạn tốc độ yêu cầu theo người dùng cho các yêu cầu đã xác thực và theo IP cho các điểm cuối công khai.
Định dạng phản hồi khi giới hạn tốc độ yêu cầu
Khi vượt quá giới hạn tốc độ yêu cầu, trả về mã trạng thái 429 với định dạng lỗi RFC 9457.
Cấu trúc phản hồi
{
"type": "https://petstoreapi.com/errors/rate-limit-exceeded",
"title": "Vượt quá giới hạn tốc độ yêu cầu",
"status": 429,
"detail": "Bạn đã vượt quá giới hạn tốc độ yêu cầu. Vui lòng thử lại sau.",
"instance": "/v1/pets",
"retryAfter": 120,
"limit": 100,
"remaining": 0,
"reset": 120,
"window": "1h"
}
Tiêu đề
429 Too Many Requests
RateLimit-Limit: 100
RateLimit-Remaining: 0
RateLimit-Reset: 120
Retry-After: 120
Retry-After: Cho client biết khi nào có thể thử lại (tính bằng giây).
Kiểm tra giới hạn tốc độ yêu cầu với Apidog
Apidog giúp bạn kiểm tra hành vi giới hạn tốc độ yêu cầu.
Các kịch bản kiểm tra
1. Sử dụng bình thường:
Gửi 50 yêu cầu → Tất cả đều thành công
Kiểm tra RateLimit-Remaining giảm
2. Vượt quá giới hạn:
Gửi 101 yêu cầu → Yêu cầu thứ 101 trả về 429
Xác minh định dạng phản hồi lỗi
Kiểm tra tiêu đề Retry-After
3. Hành vi đặt lại:
Vượt quá giới hạn → Chờ đặt lại → Xác minh giới hạn được khôi phục
4. Các cấp độ khác nhau:
Kiểm tra gói miễn phí (100/giờ)
Kiểm tra gói chuyên nghiệp (10.000/giờ)
Xác minh giới hạn được thực thi chính xác
Ví dụ kiểm tra với Apidog
// Kiểm tra các tiêu đề giới hạn tốc độ yêu cầu
pm.test("Rate limit headers present", () => {
pm.response.to.have.header("RateLimit-Limit");
pm.response.to.have.header("RateLimit-Remaining");
pm.response.to.have.header("RateLimit-Reset");
});
// Kiểm tra khi vượt quá giới hạn tốc độ yêu cầu
pm.test("Returns 429 when limit exceeded", () => {
// Thực hiện 101 yêu cầu
for (let i = 0; i < 101; i++) {
pm.sendRequest("GET /v1/pets");
}
pm.response.to.have.status(429);
});
Các thực hành tốt nhất về giới hạn tốc độ yêu cầu
1. Sử dụng tiêu đề tiêu chuẩn
Sử dụng các tiêu đề IETF tiêu chuẩn, không phải các tiêu đề X- tùy chỉnh.
2. Trả về 429, không phải 403
429 có nghĩa là "quá nhiều yêu cầu". 403 có nghĩa là "bị cấm". Đừng nhầm lẫn chúng.
3. Bao gồm Retry-After
Cho client biết khi nào họ có thể thử lại.
4. Ghi lại các giới hạn của bạn trong tài liệu
Hiển thị rõ ràng các giới hạn tốc độ yêu cầu trong tài liệu.
5. Cung cấp các cấp độ khác nhau
Gói miễn phí: giới hạn thấp. Gói trả phí: giới hạn cao hơn.
6. Giới hạn tốc độ yêu cầu theo người dùng, không phải IP
Giới hạn theo người dùng chính xác và công bằng hơn.
7. Cho phép các đợt tăng đột biến
Xô chứa mã thông báo cho phép các đợt tăng đột biến hợp lý mà không làm ảnh hưởng đến việc sử dụng bình thường.
8. Giám sát các lần vượt giới hạn tốc độ yêu cầu
Theo dõi tần suất client vượt giới hạn tốc độ yêu cầu. Tỷ lệ cao cho thấy có vấn đề.
9. Cung cấp điểm cuối trạng thái giới hạn tốc độ yêu cầu
GET /v1/rate-limit
200 OK
{
"limit": 100,
"remaining": 45,
"reset": 3540
}
10. Kiểm tra giới hạn tốc độ yêu cầu
Sử dụng Apidog để kiểm tra hành vi giới hạn tốc độ yêu cầu trước khi triển khai.
Kết luận
Giới hạn tốc độ yêu cầu bảo vệ API của bạn khỏi bị lạm dụng và đảm bảo việc sử dụng công bằng. Sử dụng thuật toán xô chứa mã thông báo với các tiêu đề IETF tiêu chuẩn (RateLimit-Limit, RateLimit-Remaining, RateLimit-Reset). Trả về mã trạng thái 429 Too Many Requests với định dạng lỗi RFC 9457 khi vượt quá giới hạn.
Modern PetstoreAPI triển khai giới hạn tốc độ yêu cầu một cách chính xác với hạn ngạch theo từng người dùng, các tiêu đề tiêu chuẩn và các phản hồi lỗi rõ ràng. Xem tài liệu để biết chi tiết triển khai.
Kiểm tra giới hạn tốc độ yêu cầu của bạn với Apidog để đảm bảo nó hoạt động chính xác dưới tải và xử lý các trường hợp biên một cách phù hợp.
Câu hỏi thường gặp (FAQ)
Tôi nên đặt giới hạn tốc độ yêu cầu nào?
Bắt đầu một cách thận trọng: 100 yêu cầu/giờ cho gói miễn phí, 10.000/giờ cho gói trả phí. Điều chỉnh dựa trên mẫu sử dụng và dung lượng cơ sở hạ tầng.
Tôi nên giới hạn tốc độ yêu cầu theo IP hay người dùng?
Giới hạn tốc độ yêu cầu theo người dùng (khóa API) cho các yêu cầu đã xác thực. Chỉ sử dụng giới hạn tốc độ yêu cầu dựa trên IP cho các điểm cuối công khai.
Điều gì xảy ra nếu một client vượt quá giới hạn tốc độ yêu cầu?
Trả về mã trạng thái 429 Too Many Requests kèm theo tiêu đề Retry-After. Đừng chặn client vĩnh viễn—hãy cho phép họ thử lại sau khi khoảng thời gian đặt lại.
Làm cách nào để xử lý giới hạn tốc độ yêu cầu cho webhooks?
Webhooks là giữa máy chủ với máy chủ, vì vậy giới hạn tốc độ yêu cầu nên cao hơn. Cân nhắc các giới hạn riêng biệt cho webhooks so với các cuộc gọi API thông thường.
Tôi có nên giới hạn tốc độ yêu cầu cho các dịch vụ nội bộ không?
Có, nhưng với giới hạn cao hơn nhiều. Giới hạn tốc độ yêu cầu ngăn chặn các lỗi dây chuyền ngay cả trong các hệ thống nội bộ.
Làm cách nào để kiểm tra giới hạn tốc độ yêu cầu?
Sử dụng Apidog để gửi nhiều yêu cầu và xác minh các phản hồi 429, các tiêu đề giới hạn tốc độ yêu cầu và hành vi đặt lại.
Điều gì xảy ra nếu API của tôi nằm sau CDN?
Bộ nhớ đệm CDN giúp giảm tải, nhưng bạn vẫn cần giới hạn tốc độ yêu cầu cho các trường hợp không có trong bộ nhớ đệm và các yêu cầu POST/PUT/DELETE.
Làm cách nào để triển khai giới hạn tốc độ yêu cầu trên nhiều máy chủ?
Sử dụng một kho dữ liệu chia sẻ (Redis, Memcached) để theo dõi giới hạn tốc độ yêu cầu trên tất cả các máy chủ. Không sử dụng bộ nhớ cục bộ—nó sẽ không hoạt động trong các hệ thống phân tán.
