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

Bài trước tôi đã chia sẻ kinh nghiệm thực tế về percentile latency trong DevOps: chọn p95 hay p99 hay p99.9 khi benchmark? cũng không ngờ mọi người lại ủng hộ nhiều và có cả các bác lấy làm tài liệu training nội bộ. Thật sự rất cảm ơn mọi người đã trân trọng những kinh nghiệm tôi chia sẻ.

d0263df6-ceec-4ee6-9734-8c098c95510c

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 DDoS bắn 1 triệu RPS, mà đến từ vài client gửi request body cực lớn, lặp đi lặp lại.

  • RPS không cao.
  • Nhưng CPU gateway tăng.
  • Memory lên xuống mạnh.
  • Upstream bắt đầu 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.

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

Thật sự những bài benchmark như thế này thông thường mọi người rất khó để thấy được ai chia sẻ thực tế (mọi người có thể research thậm trí cả tiếng Anh) vì nó gần như là tài liệu nội bộ và làm rất nhiều công sức.

Cũng vì được các sếp DevOps VietNam cho xem phần khảo sát lắng nghe ý kiến cộng đồng nên biết được mức độ phân hóa trình độ tôi mới biết chia sẻ cái gì nó thực tế mà hữu ích. Anh em càng tích cực đồng hành cùng DevOps VietNam thì ngày càng có nhiều hoạt động giá trị, bác nào muốn chia sẻ cứ mạnh dạn apply như tôi nhé ( ‾ʖ̫‾ )

Bài toán thực tế

Hệ sinh thái trong doanh nghiệp chắc mọi người cũng hiểu, bên tôi cũng vậy có nhiều hệ thống, đây cũng chỉ là một trong đó, hệ thống có 3 loại endpoint:

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

Vậy nên limit XMB cho mọi thứ (ví dụ X=1) mà mọi người có thể thấy rất nhiều tại các website cho phép upload và nghe có vẻ an toàn nhưng có thể phá use case upload làm ảnh hưởng không ít hệ thống.

Lúc này sẽ cần phải trả lời 2 câu hỏ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ì liệu 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 (có thể thay bằng Envoy/HAProxy, tôi test nginx vì hệ thống này dùng vậy)
  • Upstream: service backend đơn giản (nhận body và trả HTTP 200)

Cấu hình mỗi servers (anh em hay làm benchmark thì cũng biết rồi đây là con số phổ thông, còn sâu hơn tại sao thì nếu mọi người cần tôi làm một bài viết riêng nhé):

  • 4 vCPU / 8GB RAM
  • cùng LAN (latency thấp)

Upstream có 2 mode:

  • Mode 1 (Full Body Processing): mô phỏng logic ứng dụng thực tế (thực hiện Parse JSON, xử lý Multipart…).
  • Mode 2 (Early Response/Short-circuiting): pstream phản hồi ngay lập tức mà không xử lý Body, để đo lường chính xác hiệu quả bảo vệ và khả năng Request Filtering của Gateway.

Tôi test theo 2 nhóm Traffic Mix (tổ hợp lưu lượng) để mô phỏng hành vi người dùng thực tế:

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

RPS tổng chỉ khoảng 1k-3k rps thôi. Vì câu chuyện này không cần rps cao, nó cần body to.

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

Tôi chia 3 profile phù hợp với hệ thống của bên tôi (cũng gọi là bao quát được hầu hết các khoảng hệ thống thông thường sẽ gặp)

Profile A: Strict

  • Limit 1MB
  • Dùng cho API JSON thông thường

Profile B: Balanced

  • Limit 10MB
  • Đủ cho ảnh/PDF nhỏ

Profile C: Loose

  • Limit 50MB
  • Dành riêng cho các endpoint hỗ trợ multipart upload hoặc file lớn

Tôi benchmark:

  • 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

Đấy cũng là một phần lý do tại sao tôi lại chia sẻ bài percentile latency trong DevOps: chọn p95 hay p99 hay p99.9 khi benchmark? trước vì khi mọi người hiểu rõ hiểu sâu các chỉ số rồi thì hiểu các bài như thế này nó dễ dàng hơn và hiểu được công sức làm ra được một bài như này là không ít

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

Trước hết, tôi chạy 100% traffic tốt để đảm bảo 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

Vậy là bình thường, chỉ đặt limit cao/thấp không khác nhiều nếu không có abuse.

Trộn 5% request xấu (20MB) bắt đầu đáng nói

Tôi bắn 95% request hợp lệ (5-10KB) và 5% request vượt ngưỡng (20MB).

Profile A: limit 1MB

Request vượt ngưỡng bị chặn ngay lập tức tại tầng Gateway với mã lỗi HTTP 413 (Payload Too Large) trước khi dữ liệu được chuyển tiếp (forward) đến Upstream.

Metric Giá 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ờ cơ chế Early Rejection (từ chối sớm) tại tầng Gateway, hệ thống giảm thiểu được Resource Overhead do không phải thực hiện quá trình Request Buffering cho các Payload dung lượng lớn. Điều này giúp bảo vệ tài nguyên tính toán và băng thông nội bộ (Internal Bandwidth), đảm bảo hiệu năng ổn định cho các luồng Traffic hợp lệ.

Hay dễ hiểu là traffic tốt gần như không bị ảnh hưởng. Gateway đỡ phải gánh body to, cố gắng viết sao nông văn dền nhất để cả các bạn mới cũng có thể hiểu nhé…

Profile B: limit 10MB

Ở cấu hình này, các Request 20MB vẫn bị từ chối bằng mã lỗi HTTP 413. Tuy nhiên, hệ thống bắt đầu xuất hiện Resource Overhead tại tầng Gateway.

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

Tùy thuộc vào cấu hình Request Body Buffering, Gateway có thể phải tiếp nhận và lưu trữ tạm thời một phần dữ liệu vào RAM hoặc Disk Buffer trước khi xác định được Payload vượt ngưỡng. Quá trình I/O này làm gia tăng độ trễ xử lý.

Profile C: limit 50MB

Lúc này các request 20MB được phép Forward trực tiếp xuống tầng Upstream. Đây là cấu hình gây rủi ro cao nhất cho tính ổn định của hệ thống.

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

Upstream Saturation: Tầng Upstream bị quá tải tài nguyên do phải xử lý quá trình khử Deserialization và cấp phát bộ nhớ cho các Large Payloads.

Và thấy rõ là 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)

Tôi giảm tỉ lệ xuống còn 2% nhưng body lên 80MB.

  • Limit 1MB/10MB: hệ thống gần như vẫn ổn (chặn sớm)
  • Limit 50MB: vẫn pass một phần traffic xấu (đến ngưỡng), và cái giá trả bằng p99 và timeout

Ở profile Loose, tôi thấy:

  • Gateway memory nhấp nhô mạnh (buffer)
  • Upstream queue dài
  • Traffic tốt bị kẹt theo

Như vậy là rõ ràng abuse kiểu này không cần nhiều request. Nó chỉ cần vài request Heavy/Slow Requests to và chậm, là đủ mất ổn định dịch vụ rồi.

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

Đây là chỗ khá nhiều team hay sai, hay nói đúng hơn là có biết nhưng chưa biết đặt sao cho hợp lý:

  • Set một con số global (ví dụ 50MB) cho toàn bộ API
  • cho chắcđỡ bị user than

Nhưng thực tế đúng hơn sẽ là

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

Và nếu upload lớn, tôi khuyên mọi người là tránh upload qua gateway bằng cách: presigned URL (S3/MinIO) thì 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: request body to mà client gửi chậm thì phải có client_body_timeout/request timeout để cắt.

  3. Rate limit theo IP/route: body lớn mà bắn liên tục từ vài IP thì rate limit + body limit đi cùng mới đủ combo.

Thêm tí nữa: 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 + distribution body size (để biết limit đang đúng chưa)

Kết luận: tóm lại chốt ngưỡng nào?

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

  • Default API: 1MB
  • Upload endpoint: 10MB (nếu sản phẩm chỉ cần ảnh/PDF vừa)
  • Nếu thật sự cần upload lớn: đừng để đi qua app, chuyển sang presigned.

Còn tôi cũng đã benchmark rõ ràng rồi tùy bài toán mọi người con số này thay đổi một chút cho phù hợp. Tóm lại là 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.

Nếu mọi người thấy hữu ích tôi cũng có thể chia sẻ sâu thêm về phần này và thật sự mọi người rất nên nâng cao kiến thức về Observability liên quan đến Metrics, Logs và Traces. Kể cả những bạn mới nên sự kiện Observability with Datadog mà các sếp DevOps VietNam mang đến cho anh em, diễn giả đến từ doanh nghiệp hàng đầu thế giới về observability chia sẻ từ nền tảng rất đáng học hỏi.

Thông tin nổi bật

Sự kiện phát trực tiếp​

Event Thumbnail

Báo cáo quan trọng

Article Thumbnail
Article Thumbnail
Chia sẻ bài viết:
Theo dõi
Thông báo của
0 Góp ý
Được bỏ phiếu nhiều nhất
Mới nhất Cũ nhất
Phản hồi nội tuyến
Xem tất cả bình luận

Tiêu điểm chuyên gia