Hieu Solutions
Công nghệ

Benchmark thực tế: chỉ 5% request to cũng đủ phá p99 toàn hệ thống

1 tháng 1, 1970
28 lượt xem
Benchmark thực tế: chỉ 5% request to cũng đủ phá p99 toàn hệ thống

Benchmark thực tế: chỉ 5% request to cũng đủ phá p99 toàn hệ thống

Long Bùi  • 

Hero image

Bài trước tôi đã chia sẻ kinh nghiệm thực tế về percentile latency trong DevOps. Trong bài này, bài toán đến từ vài lần hệ thống bên tôi bị bất ổn, không phải do DDoS bắn RPS cao, mà do vài client gửi request body cực lớn lặp đi lặp lại:

  • RPS không cao.
  • CPU gateway tăng.
  • Memory nhấp nhô mạnh.
  • Upstream timeout vì phải đọc body lớn.
  • Và tệ nhất: p99 của toàn hệ thống xấu đi dù đa số người dùng gửi request bình thường.

Trong quá trình observability, nguyên nhân nằm ở tầng gateway nên tôi tiến hành benchmark giới hạn request body để chọn ngưỡng đủ an toàn, không ảnh hưởng user thật nhưng chặn được abuse và bảo vệ upstream.

Bài toán thực tế

Hệ thống có 3 loại endpoint chính:

  1. API JSON bình thường (body vài KB)
  2. Upload file (ảnh/PDF) qua API (một số endpoint nhận multipart)
  3. Một vài endpoint nội bộ có body lớn hơn (batch)

Vì vậy, đặt một giới hạn X MB cho mọi thứ (ví dụ X=1) có vẻ an toàn nhưng có thể phá use case upload. Cần trả lời:

  1. Nếu gateway limit thấp (1MB) thì hệ thống có khỏe hơn rõ rệt không?
  2. Nếu gateway limit cao (10MB/50MB) thì abuse vẫn đủ làm hệ thống mệt không?

Setup benchmark

Tôi dựng 3 servers (để không bị nhiễu loopback):

  • Loadgen: bắn request
  • Gateway: Nginx
  • Upstream: backend đơn giản (nhận body và trả HTTP 200)

Cấu hình mỗi server: 4 vCPU / 8GB RAM, cùng LAN (latency thấp).

Upstream có 2 mode:

  • Mode 1 (Full Body Processing): mô phỏng logic thật (parse JSON, xử lý multipart).
  • Mode 2 (Early Response/Short-circuiting): upstream phản hồi ngay mà không xử lý body (để đo hiệu quả bảo vệ của Gateway).

Tôi test theo 2 nhóm Traffic Mix để mô phỏng hành vi:

  • Traffic tốt: 95% request body ~ 2-10KB
  • Traffic xấu: 5% request body lớn: 5MB/20MB/80MB (tuỳ test)

Tổng RPS khoảng 1k-3k. Câu chuyện cần body to, không cần RPS cao.

Các ngưỡng limit đem ra test

Tôi chia 3 profile:

  • Profile A: Strict — Limit 1MB (dùng cho API JSON bình thường)
  • Profile B: Balanced — Limit 10MB (đủ cho ảnh/PDF nhỏ)
  • Profile C: Loose — Limit 50MB (dành cho multipart/file lớn)

Tôi đo: p95/p99 latency của traffic tốt, CPU/RAM gateway + upstream, error rate (HTTP 413/4xx cho request xấu), số request tốt bị ảnh hưởng khi có request xấu.

Kết quả benchmark

Khi không có request xấu (baseline)

Chạy 100% traffic tốt để kiểm tra limit không làm chậm hệ thống:

Limit p99 (traffic tốt) CPU gateway Ghi chú
1MB~12-18ms~25-35%ok
10MB~12-18ms~25-35%ok
50MB~12-18ms~25-35%ok

Kết luận: nếu không có abuse, đặt limit cao hay thấp không khác nhiều.

Trộn 5% request xấu (20MB)

Tôi chạy 95% request hợp lệ (5-10KB) và 5% request 20MB.

Profile A: limit 1MB

Request vượt ngưỡng bị chặn ngay tại Gateway bằng HTTP 413 trước khi forward đến Upstream.

MetricGiá trị
p99 traffic tốt~15-25ms
CPU gateway~35-45%
CPU upstream~30-40%
Error (request xấu)~5% (HTTP 413)
Error (request tốt)~0%

Nhờ Early Rejection tại Gateway, hệ thống giảm thiểu resource overhead vì không buffer payload lớn, bảo vệ tài nguyên và băng thông cho traffic hợp lệ.

Profile B: limit 10MB

Request 20MB vẫn bị từ chối bằng HTTP 413, nhưng bắt đầu xuất hiện resource overhead tại Gateway.

MetricGiá trị
p99 traffic tốt~20-35ms
CPU gateway~45-60%
CPU upstream~35-45%
Error request tốt~0-0.2%

Gateway có thể phải lưu tạm một phần dữ liệu vào RAM hoặc disk trước khi xác định payload vượt ngưỡng, gây I/O và tăng độ trễ.

Profile C: limit 50MB

Request 20MB được phép forward xuống Upstream — rủi ro cao nhất cho ổn định.

MetricGiá trị
p99 traffic tốt~60-120ms
CPU gateway~60-80%
CPU upstream~70-95%
Error request tốt~0.8-2% (timeout)

Chỉ cần 5% request 20MB là đủ làm p99 của traffic tốt xấu đi nếu gateway cho pass.

Trộn 2% request siêu xấu (80MB)

Khi giảm tỷ lệ xuống 2% nhưng tăng body lên 80MB:

  • Limit 1MB/10MB: hệ thống gần như ổn (bị chặn sớm).
  • Limit 50MB: vẫn pass một phần traffic xấu, dẫn đến p99 và timeout xấu.

Ở profile Loose, thấy gateway memory nhấp nhô mạnh (buffer), upstream queue dài, traffic tốt bị kẹt theo.

Kết luận: abuse kiểu này không cần nhiều request. Vài request heavy/slow đủ làm mất ổn định dịch vụ.

Điều cực kỳ quan trọng: limit không nên là một con số cho toàn hệ

Nhiều team thường đặt một con số global (ví dụ 50MB) cho toàn API vì sợ user than phiền. Thực tế nên:

Chia limit theo route

  • /api/* JSON bình thường: 1MB
  • /upload/* hoặc endpoint multipart: 10MB hoặc 50MB (tùy sản phẩm)
  • Batch nội bộ: tách riêng, có auth mạnh hơn, limit khác

Nếu upload lớn, tránh upload qua gateway bằng presigned URL (S3/MinIO) — gateway chỉ xử lý metadata, không gánh data.

Một vài thứ ẩn làm limit hiệu quả hoặc vô nghĩa

  1. client_max_body_size chỉ là một phần: nếu gateway vẫn buffer body lớn xuống disk/mem trước khi chặn, bạn vẫn bị ăn I/O.
  2. Timeout phải đi cùng limit: body lớn mà client gửi chậm thì cần client_body_timeout/request timeout để cắt.
  3. Rate limit theo IP/route: body lớn bắn liên tục từ vài IP thì rate limit + body limit là combo cần thiết.

3 thứ nên làm

  1. Set body limit theo từng route.
  2. Set timeout cho client body send (để chặn slow upload abuse).
  3. Theo dõi metric 413/4xx + phân phối kích thước body (để biết limit đúng hay chưa).

Kết luận: chốt ngưỡng nào?

Nếu hệ thống giống tôi (đa số JSON nhỏ, có vài upload):

  • Default API: 1MB
  • Upload endpoint: 10MB (nếu chỉ cần ảnh/PDF vừa)
  • Nếu cần upload lớn: đừng để qua app, dùng presigned.

Body limit không phải để hạn chế user, mà để bảo vệ p99 của user tốt khỏi vài request xấu.

Tags: API Gateway, Benchmark, Case Study

💬 Bạn thấy bài viết này thế nào?

Chia sẻ suy nghĩ của bạn hoặc kết nối với tôi trên mạng xã hội!