So sánh các giải pháp Load Testing hiện nay: Apache JMeter, k6 và Locust
Trước các đợt sale lớn hay ngày ra mắt tính năng mới, backend của bạn chịu được bao nhiêu traffic đồng thời? Nếu không đo tải trước, hệ thống rất dễ ‘sập’ ngay khi lượng truy cập vừa chạm đỉnh. Lúc này, chọn đúng công cụ kiểm thử sẽ giúp team tiết kiệm hàng chục giờ debug.
Hiện nay có 3 giải pháp phổ biến nhất mà các kỹ sư backend thường cân nhắc:
- Apache JMeter: Công cụ ‘lão làng’ viết bằng Java. Điểm mạnh là giao diện kéo thả và kho plugin đồ sộ, nhưng cấu hình qua file XML rất nặng nề và khó track bằng Git.
- k6 (Grafana): Lựa chọn hiện đại viết bằng Go. k6 dùng JavaScript (ES6) để viết script, cực kỳ nhẹ và sinh ra để tích hợp vào pipeline CI/CD.
- Locust: Framework mã nguồn mở viết 100% bằng Python. Bạn định nghĩa hành vi user hoàn toàn bằng code Python thông thường, đi kèm Web UI theo dõi chỉ số realtime trực quan.
Phân tích ưu và nhược điểm của từng công cụ
Mỗi công cụ đều có thế mạnh riêng tùy thuộc vào hạ tầng và thói quen của team:
1. Apache JMeter
- Ưu điểm: Hỗ trợ hầu hết giao thức mạng (HTTP, JDBC, FTP, TCP, LDAP). Tài liệu phong phú, cộng đồng hỗ trợ lớn.
- Nhược điểm: Mô hình 1 thread cho mỗi user ngốn rất nhiều RAM/CPU khi giả lập từ 2.000 – 5.000 user trở lên. File XML cồng kềnh, khó merge code khi làm việc nhóm.
2. k6
- Ưu điểm: Tốc độ sinh tải cực nhanh nhờ runtime Go tối ưu. Script JS dễ đọc, đẩy metric trực tiếp sang Prometheus và Grafana rất mượt.
- Nhược điểm: Không chạy trên Node.js runtime đầy đủ nên không thể
npm installtùy ý mọi thư viện bên ngoài. Bản mã nguồn mở không kèm Web UI cục bộ.
3. Locust
- Ưu điểm: Viết code Python thuần túy. Bạn thoải mái dùng
requests,faker,redishay bất kỳ package nào trong hệ sinh thái Python. Nhờ kiến trúc coroutine trêngevent, một máy dev 4 core có thể dễ dàng tạo 5.000 – 10.000 user ảo (Virtual Users). Web UI tích hợp sẵn, hiển thị biểu đồ realtime mà không cần setup thêm hạ tầng. - Nhược điểm: Thông lượng thô (raw throughput) trên 1 core đơn lẻ thấp hơn k6. Tuy nhiên, Locust giải quyết gọn gàng vấn đề này bằng chế độ phân tán Master – Worker chỉ với một cờ lệnh CLI.
Vì sao team Python nên chọn Locust?
Nếu tech stack chính của bạn là Python, Locust mang lại trải nghiệm tiện lợi nhất. Bạn không cần làm quen giao diện GUI phức tạp hay học thêm cú pháp domain-specific language mới.
Mọi kịch bản phức tạp đều xử lý trực tiếp bằng code: từ đăng nhập lấy token JWT, query database chuẩn bị dữ liệu test, cho đến sinh mock data với Faker. Vòng lặp for, rẽ nhánh if/else, xử lý exception — tất cả đều là Python quen thuộc.
Hướng dẫn triển khai kiểm thử tải API với Locust
Bước 1: Cài đặt Locust
Tạo một virtual environment riêng và cài đặt Locust qua pip:
# Tạo và kích hoạt môi trường ảo
python3 -m venv venv
source venv/bin/activate
# Cài đặt Locust
pip install locust
# Kiểm tra phiên bản cài đặt thành công
locust --version
Bước 2: Viết kịch bản kiểm thử API (locustfile.py)
Tạo file locustfile.py trong thư mục dự án. Chúng ta sẽ mô phỏng một luồng thương mại điện tử: đăng nhập lấy token JWT, sau đó duyệt danh sách sản phẩm và xem profile cá nhân.
import json
from locust import HttpUser, task, between
class EcommerceUser(HttpUser):
# Thời gian nghỉ ngẫu nhiên giữa các request: từ 1 đến 3 giây
wait_time = between(1, 3)
token = None
def on_start(self):
"""Chạy 1 lần khi mỗi Virtual User khởi tạo (dùng để đăng nhập)"""
payload = {
"username": "test_user",
"password": "Secret@123"
}
headers = {"Content-Type": "application/json"}
response = self.client.post("/api/v1/auth/login", json=payload, headers=headers)
if response.status_code == 200:
self.token = response.json().get("access_token")
else:
response.failure("Đăng nhập thất bại, không lấy được token!")
@task(3)
def get_products(self):
"""Task duyệt sản phẩm (trọng số 3: chạy nhiều gấp 3 lần task profile)"""
headers = {"Authorization": f"Bearer {self.token}"} if self.token else {}
with self.client.get("/api/v1/products?page=1&limit=20", headers=headers, catch_response=True) as res:
if res.status_code == 200:
res.success()
else:
res.failure(f"Lỗi lấy danh sách sản phẩm: {res.status_code}")
@task(1)
def get_user_profile(self):
"""Task xem thông tin cá nhân (trọng số 1)"""
headers = {"Authorization": f"Bearer {self.token}"} if self.token else {}
self.client.get("/api/v1/users/me", headers=headers, name="/api/v1/users/me")
Điểm mấu chốt trong script trên:
HttpUser: Class đại diện cho một người dùng ảo, tích hợp sẵn client HTTP quản lý session và cookie.wait_time = between(1, 3): Mô phỏng think-time của người dùng thực tế, tránh tình trạng user ảo spam request liên tục phi thực tế.on_start: Lifecycle hook chạy trước khi bắt đầu các task, phù hợp cho luồng login lấy access token.@task(weight): Định nghĩa tỉ lệ phân bổ request. Trọng số@task(3)sẽ nhận 75% tổng lượt gọi so với 25% của@task(1).catch_response=True: Cho phép can thiệp đánh dấu request fail nếu dữ liệu trả về sai business logic, dù status code vẫn là 200 OK.
Bước 3: Chạy test và điều khiển qua Web UI
Khởi chạy Locust bằng lệnh sau trong terminal:
locust -f locustfile.py --host http://localhost:8000
Truy cập vào http://localhost:8089 trên trình duyệt và điền thông số:
- Number of users: 200 (tổng số user ảo muốn giả lập).
- Ramp-up (users started/second): 10 (mỗi giây tăng thêm 10 user cho đến khi đủ 200).
- Host: URL gốc của backend cần đo tải.
Bấm Start swarming để bơm tải và quan sát biểu đồ thời gian thực.
Bước 4: Chạy test chế độ Headless trên CI/CD
Khi tích hợp vào pipeline GitHub Actions hoặc GitLab CI, bạn chạy chế độ không cần UI:
locust -f locustfile.py \
--headless \
--users 100 \
--spawn-rate 10 \
--run-time 3m \
--host http://localhost:8000 \
--html report.html
Lệnh trên sẽ chạy test với 100 user trong 3 phút và tự động xuất báo cáo trực quan ra file report.html.
Cách phân tích các chỉ số hiệu năng API
Khi bài test đang chạy, bạn cần tập trung vào 3 chỉ số then chốt sau:
1. Requests Per Second (RPS / Throughput)
Chỉ số này đo lường số lượng request backend xử lý thành công mỗi giây. Nếu số user tăng nhưng RPS đi ngang hoặc tụt dốc, backend đã chạm ngưỡng nghẽn (bottleneck) — thường do pool kết nối database bị cạn hoặc CPU server chạm 100%.
2. Response Time (Latency Percentiles: 50%, 95%, 99%)
Đừng chỉ dựa vào thời gian phản hồi trung bình (Average Response Time). Con số này thường bị ‘làm đẹp’ bởi các request nhẹ 10ms, che lấp các request nặng bị treo 5 giây.
- 50th Percentile (Median): Trải nghiệm của 50% người dùng phổ thông.
- 95th Percentile: 95% request hoàn thành nhanh hơn mốc này. Đây là tiêu chuẩn vàng để đánh giá SLA (ví dụ: cam kết p95 < 250ms).
- 99th Percentile: Đo lường các ca xấu nhất, thường xảy ra khi database dính table lock hoặc Garbage Collection bị kích hoạt.
3. Failure Rate (% lỗi)
Tỉ lệ request trả mã lỗi 5xx hoặc bị timeout. Trong một bài test tiêu chuẩn, tỉ lệ lỗi chấp nhận được nên dưới 1%. Khi con số này vọt lên 5-10%, bạn đã tìm ra điểm gãy (breaking point) của hệ thống.
Checklist tối ưu trước khi chạy Load Test
- Tách biệt máy test và server: Đặt Locust và server backend ở 2 host riêng biệt. Nếu chạy chung, chính Locust sẽ tranh chấp CPU của server và làm sai lệch kết quả.
- Chạy trên môi trường tương đương Production: Test trên Staging/UAT có cấu hình RAM, CPU và volume database gần với thực tế nhất có thể. Không đo tải trên máy local qua localhost vì bị nghẽn I/O ảo.
- Theo dõi tài nguyên server: Bật sẵn công cụ monitor (như
htop, Prometheus, Datadog) để soi CPU, RAM, IOPS đĩa cứng và Connection Pool của database trong suốt quá trình bơm tải.

