Mở đầu — vì sao bài này quan trọng
Hãy tưởng tượng bạn vừa viết xong spec cho một API tra cứu điểm tín dụng. Mọi thứ hoạt động hoàn hảo trong môi trường test. Rồi đến ngày go-live, một đối tác tích hợp sai vòng lặp và bắn 50.000 request/giây vào endpoint của bạn. Database sập, toàn bộ hệ thống ngừng phục vụ cho mọi khách hàng khác, không chỉ riêng đối tác kia. Lúc đó câu hỏi đầu tiên mà sếp hỏi không phải "ai code sai" mà là "tại sao spec của chúng ta không có rate limit?".
Đây chính xác là lý do một Technical BA cần hiểu Rate Limiting và API Throttling. Đây không phải chuyện của riêng developer hay DevOps. Khi bạn viết spec cho một API, bạn phải trả lời được: mỗi client được gọi bao nhiêu lần trong một khoảng thời gian? Khi vượt giới hạn thì hệ thống phản hồi thế nào? Giới hạn này có khác nhau giữa gói miễn phí và gói trả phí không? Những câu hỏi này nằm ngay trong vùng trách nhiệm của bạn, và nếu bạn không nêu ra trong spec, sẽ chẳng có ai nghĩ tới cho đến khi sự cố xảy ra.
Bài này sẽ giúp bạn hiểu bản chất rate limiting, phân biệt các thuật toán phổ biến, và quan trọng nhất là biết cách diễn đạt chúng thành yêu cầu rõ ràng trong tài liệu của mình.
Khái niệm cốt lõi
Rate Limiting và Throttling khác nhau ở đâu?
Hai thuật ngữ này thường được dùng lẫn lộn, nhưng có sắc thái khác nhau mà bạn nên nắm.
Rate Limiting là việc đặt một giới hạn cứng: client X được phép gọi tối đa N request trong khoảng thời gian T. Vượt qua là từ chối thẳng (thường trả về mã lỗi HTTP 429 — "Too Many Requests").
Throttling mang nghĩa "điều tiết" mềm hơn: thay vì từ chối hẳn, hệ thống làm chậm lại, xếp hàng đợi, hoặc giảm dần tốc độ phục vụ. Ví dụ thay vì trả 429 ngay, hệ thống giữ request lại vài trăm mili-giây rồi mới xử lý.
Trong thực tế, đa số tài liệu và sản phẩm dùng "rate limiting" như từ bao trùm cho cả hai. Khi viết spec, bạn chỉ cần làm rõ hành vi mong muốn: từ chối ngay hay xếp hàng đợi.
Ba lý do cốt lõi phải có rate limiting
Một là bảo vệ backend khỏi traffic spike. Mọi hệ thống đều có ngưỡng chịu tải. Một đợt traffic đột biến — dù do tấn công DDoS, do một bug ở client, hay đơn giản do chiến dịch marketing thành công ngoài dự kiến — có thể làm sập toàn bộ. Rate limiting là tấm chắn đầu tiên, giúp hệ thống "hy sinh" một phần request thay vì gục ngã hoàn toàn.
Hai là đảm bảo fair use cho mọi client. Nếu không có giới hạn, một client tham lam có thể chiếm hết tài nguyên, khiến những client khác bị đói. Rate limiting đảm bảo mỗi người chơi đều được một phần công bằng của chiếc bánh.
Ba là phục vụ monetization — kiếm tiền theo tier. Đây là góc nhìn business mà BA cần đặc biệt lưu tâm. Rất nhiều mô hình API kiếm tiền bằng cách bán quyền gọi nhiều hơn: gói Free cho 100 request/ngày, gói Pro cho 10.000 request/ngày, gói Enterprise không giới hạn. Rate limiting chính là cơ chế kỹ thuật biến gói cước trên giấy thành thực thi được.
Các thuật toán rate limiting phổ biến
Đây là phần kỹ thuật cốt lõi. Bạn không cần code chúng, nhưng phải hiểu để chọn đúng và viết spec đúng.
1. Fixed Window (cửa sổ cố định). Đơn giản nhất: chia thời gian thành các khung cố định (ví dụ mỗi phút), đếm số request trong khung hiện tại. Đến giây 00 của phút mới, bộ đếm reset về 0. Ưu điểm là dễ hiểu, dễ triển khai. Nhược điểm chí mạng là vấn đề "burst ở rìa cửa sổ": nếu giới hạn là 100 request/phút, client có thể bắn 100 request vào giây 12:00:59 và thêm 100 request vào giây 12:01:00 — tức 200 request trong vòng 2 giây mà vẫn không vi phạm luật.
2. Sliding Window (cửa sổ trượt). Khắc phục nhược điểm trên bằng cách tính giới hạn trên một khoảng thời gian trượt liên tục thay vì khung cố định. Tại mỗi thời điểm, hệ thống nhìn lại đúng 60 giây vừa qua và đếm. Mượt hơn, công bằng hơn, nhưng tốn tài nguyên tính toán và lưu trữ hơn vì phải nhớ timestamp của từng request (hoặc dùng phép xấp xỉ sliding window log/counter).
3. Token Bucket (xô token). Hình dung một chiếc xô chứa token, được đổ đầy lại với tốc độ cố định (ví dụ 10 token/giây), dung lượng tối đa 100 token. Mỗi request tiêu tốn 1 token. Nếu xô còn token thì cho qua, hết token thì từ chối. Điểm hay của token bucket là cho phép burst có kiểm soát: nếu client im lặng một lúc, xô tích đầy token, và họ có thể bắn một loạt request nhanh — rất phù hợp với hành vi người dùng thật (thỉnh thoảng thao tác dồn dập rồi nghỉ). Đây là thuật toán phổ biến nhất trong thực tế, được AWS, Stripe và nhiều nền tảng lớn dùng.
4. Leaky Bucket (xô rò rỉ). Ngược với token bucket về triết lý. Request đổ vào xô, và xô "rò" ra với tốc độ cố định để xử lý. Nếu xô đầy (quá nhiều request dồn vào), request mới bị tràn và loại bỏ. Leaky bucket làm phẳng (smooth) đầu ra, đảm bảo tốc độ xử lý ổn định, nhưng không cho phép burst như token bucket.
Một cách ghi nhớ: Token Bucket khoan dung với burst, Leaky Bucket nghiêm khắc với tốc độ đầu ra.
Giới hạn theo chiều nào?
Một câu hỏi quan trọng mà BA hay quên: giới hạn áp dụng theo đối tượng nào?
- Theo API key / user / tenant (phổ biến nhất cho monetization).
- Theo IP address (chống abuse từ client ẩn danh, nhưng cẩn thận với NAT — nhiều người dùng chung một IP).
- Theo endpoint (endpoint nặng như xuất báo cáo có thể có giới hạn riêng, chặt hơn endpoint nhẹ).
- Theo global (tổng tải toàn hệ thống, để bảo vệ tầng hạ tầng).
HTTP response và header chuẩn
Khi từ chối, chuẩn là trả về HTTP 429 Too Many Requests. Quan trọng không kém là các header thông báo cho client biết tình trạng:
RateLimit-Limit: tổng quota.RateLimit-Remaining: số request còn lại.RateLimit-Reset: khi nào quota reset.Retry-After: client nên đợi bao nhiêu giây trước khi thử lại.
Tình huống thực tế
Tình huống 1 — Cổng thanh toán bị quá tải dịp sale
Một ví thanh toán điện tử lớn ở Việt Nam (giả định gọi là PayViet) mở API cho phép các sàn thương mại điện tử tạo mã QR thanh toán. Ban đầu họ chỉ đặt giới hạn theo Fixed Window: 600 request/phút mỗi merchant.
Đến đợt sale 11/11, một sàn lớn tích hợp lại nhằm "gom" giao dịch: cứ mỗi phút họ đợi đến giây cuối cùng rồi đẩy một loạt 600 request, sang phút mới lại bắn tiếp 600. Kết quả là cứ vào khoảnh khắc chuyển giao giữa hai phút, PayViet nhận tới 1.200 request trong vài giây, làm tầng xử lý QR nghẽn cục bộ và một số giao dịch timeout. Khách hàng cuối nhìn thấy "thanh toán thất bại" dù tiền không bị trừ — trải nghiệm tệ vào đúng ngày quan trọng nhất.
Bài học rút ra: Fixed Window có lỗ hổng burst ở rìa cửa sổ. Sau sự cố, đội PayViet chuyển sang Token Bucket với tốc độ đổ đầy 10 token/giây (tương đương 600/phút nhưng trải đều), cho phép burst tối đa 100. Burst vẫn được hỗ trợ nhưng có trần, và không còn hiện tượng dồn cục ở mốc chuyển phút. Với vai trò BA, bài học là: khi spec một giới hạn, đừng chỉ ghi con số "600/phút", hãy nghĩ đến hành vi ở các thời điểm biên.
Tình huống 2 — Monetize API theo tier ở một startup SaaS
Một startup SaaS Đông Nam Á cung cấp API trích xuất dữ liệu hóa đơn bằng AI. Họ muốn bán theo ba gói: Free (100 request/ngày), Growth (5.000 request/ngày, 2 triệu đồng/tháng), Enterprise (custom). Ban đầu sản phẩm chạy được nhưng phòng kinh doanh phàn nàn rằng nhiều khách dùng gói Free "lách" bằng cách đăng ký nhiều tài khoản để cộng dồn quota.
Khi rà soát, BA phát hiện spec gốc đặt giới hạn theo API key, nhưng không có giới hạn nào theo công ty (tenant) hay theo số điện thoại/email đã xác minh. Vậy là một người tạo 30 tài khoản Free để có 3.000 request/ngày miễn phí, phá vỡ logic monetization.
Bài học rút ra: Rate limiting phục vụ business chứ không chỉ phục vụ kỹ thuật. BA phải xác định rõ "đơn vị đếm" gắn với mô hình doanh thu. Spec sửa lại bổ sung giới hạn cấp tenant và yêu cầu xác minh danh tính trước khi cấp quota Free, đồng thời trả header RateLimit-Remaining để khách thấy rõ họ còn bao nhiêu lượt — vừa minh bạch, vừa là một cú hích tinh tế để nâng cấp gói.
Tình huống 3 — Đối tác bị 429 mà không biết tại sao
Một ngân hàng số tích hợp API định danh điện tử (eKYC) từ một nhà cung cấp bên thứ ba. API này có giới hạn 50 request/giây, nhưng tài liệu chỉ ghi một dòng "có áp dụng rate limit" mà không nêu con số, không mô tả header trả về. Khi tải tăng, hệ thống ngân hàng nhận về hàng loạt lỗi 429 nhưng client được lập trình coi 429 là lỗi "thử lại ngay lập tức" — tạo thành vòng lặp retry dồn dập, càng làm tình hình tệ hơn (hiện tượng "retry storm").
Bài học rút ra: Spec mơ hồ về rate limiting nguy hiểm ngang với không có rate limiting. Một tài liệu API tốt phải nêu rõ: con số giới hạn cụ thể, áp theo chiều nào, mã lỗi trả về, các header kèm theo, và khuyến nghị client dùng exponential backoff (đợi lâu dần sau mỗi lần thất bại) dựa trên header Retry-After. Đây đều là những điều BA phải đưa vào tài liệu, không phải để developer tự đoán.
Hướng dẫn từng bước
Khi bạn được giao viết phần rate limiting trong spec cho một API, hãy đi theo trình tự sau.
Bước 1 — Xác định mục tiêu chính. Tự hỏi: giới hạn này để bảo vệ hạ tầng, để công bằng, hay để kiếm tiền? Câu trả lời quyết định cách thiết kế. Nếu để monetization, giới hạn phải gắn với tier và đơn vị tính tiền. Nếu để bảo vệ hạ tầng, giới hạn thiên về global và per-endpoint.
Bước 2 — Chọn đơn vị đếm (scope). Theo API key, user, tenant, IP, hay endpoint? Thường là kết hợp nhiều tầng. Ghi rõ từng tầng và con số tương ứng.
Bước 3 — Xác định con số và khoảng thời gian. Đừng bịa số. Lấy dữ liệu tải thực tế hoặc dự báo từ business, cộng với capacity của hệ thống do đội kỹ thuật cung cấp. Ví dụ: "100 request/phút mỗi API key cho gói Free; 2.000 request/phút cho gói Pro".
Bước 4 — Chọn (hoặc đề xuất) thuật toán phù hợp. Cần cho phép burst tự nhiên của người dùng? Token Bucket. Cần đầu ra phẳng tuyệt đối? Leaky Bucket. Cần đơn giản và chấp nhận rủi ro biên? Fixed Window. Cần công bằng mượt mà? Sliding Window. Bạn không quyết định một mình, nhưng nên nêu đề xuất kèm lý do để thảo luận với architect.
Bước 5 — Định nghĩa hành vi khi vượt giới hạn. Trả 429 ngay, hay xếp hàng đợi (throttle)? Trả về body lỗi như thế nào? Các header gì? Khi nào reset?
Bước 6 — Mô tả trải nghiệm phía client. Khách hàng/đối tác sẽ thấy gì? Có dashboard xem quota còn lại không? Có cảnh báo khi sắp chạm trần không? Đây là phần dễ bị bỏ sót nhưng ảnh hưởng trực tiếp đến sự hài lòng.
Bước 7 — Viết acceptance criteria kiểm thử được. Ví dụ: "Khi một API key gửi request thứ 101 trong vòng 60 giây, hệ thống phải trả HTTP 429 kèm header Retry-After, và không xử lý request đó". Tiêu chí phải đo được để QA kiểm thử.
Lỗi thường gặp & mẹo
Lỗi 1 — Chỉ ghi con số mà quên hành vi. "Giới hạn 1.000 request/phút" là chưa đủ. Vượt thì sao? Header nào trả về? BA giỏi luôn mô tả đầy đủ vòng đời của một request bị từ chối.
Lỗi 2 — Quên vấn đề burst ở rìa cửa sổ. Như tình huống PayViet, Fixed Window nghe có vẻ ổn cho đến khi gặp client tinh ranh. Nếu chọn Fixed Window, hãy nêu rõ rủi ro này trong spec để mọi người ý thức.
Lỗi 3 — Nhầm rate limiting với chống DDoS. Rate limiting bảo vệ ở tầng ứng dụng theo client hợp lệ. Một cuộc tấn công DDoS quy mô lớn cần thêm lớp phòng thủ ở tầng mạng/CDN (WAF, scrubbing). Đừng hứa trong spec rằng rate limiting sẽ chống được mọi DDoS.
Lỗi 4 — Đặt giới hạn theo IP cho hệ thống có NAT. Nhiều người dùng trong một văn phòng hoặc một nhà mạng di động có thể chia sẻ chung một IP công cộng. Giới hạn theo IP có thể vô tình chặn cả nhóm người dùng hợp lệ. Cân nhắc kỹ scope.
Lỗi 5 — Không nghĩ đến môi trường phân tán. Nếu API chạy trên nhiều server, bộ đếm phải được chia sẻ chung (thường lưu ở Redis hoặc tầng gateway), nếu không mỗi server đếm riêng và giới hạn thực tế bị nhân lên theo số server. BA nên nêu yêu cầu "giới hạn áp dụng toàn cục trên toàn cụm" để tránh hiểu nhầm.
Mẹo: Luôn yêu cầu trả header Retry-After và khuyến nghị client dùng exponential backoff. Đây là cách hiệu quả nhất để ngăn "retry storm" — vòng lặp thử lại dồn dập làm hệ thống tệ thêm.
Mẹo: Phân biệt rõ giới hạn "mềm" (cảnh báo, vẫn cho qua nhưng log lại) và "cứng" (chặn thẳng). Nhiều hệ thống dùng giới hạn mềm để theo dõi và thông báo cho khách trước khi áp giới hạn cứng — một trải nghiệm thân thiện hơn.
Bài tập thực hành
Bài 1. Một API gửi SMS OTP có giới hạn 5 request/phút mỗi số điện thoại. Hãy chọn thuật toán phù hợp và giải thích vì sao. (Gợi ý: hành vi gửi OTP có cần burst không, hay cần trải đều và nghiêm khắc?)
Bài 2. Viết phần đặc tả rate limiting cho một endpoint xuất báo cáo nặng (POST /reports/export) của một SaaS có ba gói cước Free / Pro / Enterprise. Đặc tả phải nêu: scope, con số cho từng gói, thuật toán đề xuất, mã lỗi và header khi vượt, và ít nhất hai acceptance criteria kiểm thử được.
Bài 3. Cho tình huống: hệ thống của bạn chạy trên 4 server, mỗi server tự đếm độc lập với giới hạn 250 request/phút mỗi API key. Một khách hàng phàn nàn rằng họ thực tế gọi được tới gần 1.000 request/phút. Hãy giải thích nguyên nhân và viết một câu yêu cầu (requirement statement) để khắc phục.
Bài 4. So sánh Token Bucket và Leaky Bucket trong đúng một bảng ba dòng: triết lý, có cho phép burst không, tình huống phù hợp nhất. Sau đó chọn một trong hai cho API streaming dữ liệu cảm biến IoT cần đầu ra ổn định, và biện luận.
Tóm tắt
Rate limiting và throttling là cơ chế kiểm soát lượng request mà mỗi client được phép gửi trong một khoảng thời gian, phục vụ ba mục tiêu cốt lõi: bảo vệ backend khỏi quá tải, đảm bảo công bằng giữa các client, và làm nền cho mô hình kiếm tiền theo tier. Bốn thuật toán cần nắm là Fixed Window (đơn giản nhưng lỗ hổng burst ở rìa), Sliding Window (mượt và công bằng nhưng tốn kém), Token Bucket (cho phép burst có kiểm soát, phổ biến nhất), và Leaky Bucket (đầu ra phẳng, nghiêm khắc về tốc độ).
Với vai trò Technical BA, công việc của bạn không phải code thuật toán mà là biến những khái niệm này thành spec rõ ràng: xác định mục tiêu, chọn scope đếm, đặt con số có căn cứ, đề xuất thuật toán, định nghĩa hành vi khi vượt giới hạn (HTTP 429, header Retry-After, RateLimit-Remaining), và viết acceptance criteria kiểm thử được. Hãy nhớ ba bài học từ thực tế: cảnh giác với burst ở rìa cửa sổ, gắn đơn vị đếm với mô hình doanh thu, và đừng bao giờ để spec mơ hồ về giới hạn — vì sự mơ hồ ấy sẽ trở thành sự cố vào đúng ngày bạn không mong muốn nhất.