Load Testing cho hệ thống phân tán — k6, NBomber và chiến lược kiểm thử hiệu năng
Posted on: 4/25/2026 1:14:20 PM
Table of contents
- 1. Tại sao Load Testing quan trọng với hệ thống phân tán?
- 2. Các loại kiểm thử hiệu năng
- 3. Grafana k6 — Load testing hiện đại với JavaScript/TypeScript
- 4. NBomber — Load testing thuần .NET cho C# developer
- 5. So sánh k6 vs NBomber
- 6. Chiến lược Load Testing cho Microservices
- 7. Tích hợp Load Testing vào CI/CD Pipeline
- 8. Phân tích kết quả và debug bottleneck
- 9. Best Practices cho Production Load Testing
- 10. Kết luận
- Tham khảo
Bạn vừa deploy xong một microservice mới, mọi thứ hoạt động trơn tru trên staging — response time dưới 200ms, không có lỗi. Nhưng khi lên production với 10.000 concurrent users, hệ thống sụp đổ trong vòng 3 phút. Load testing chính là lớp phòng thủ giúp bạn phát hiện những điểm yếu này trước khi chúng trở thành sự cố production.
Bài viết này đi sâu vào chiến lược kiểm thử hiệu năng cho hệ thống phân tán, so sánh hai công cụ hàng đầu — Grafana k6 (JavaScript/TypeScript) và NBomber (.NET/C#) — cùng những pattern thực chiến để tích hợp load testing vào CI/CD pipeline.
1. Tại sao Load Testing quan trọng với hệ thống phân tán?
Trong kiến trúc monolith, bottleneck thường nằm ở một điểm duy nhất — database hoặc application server. Nhưng với hệ thống phân tán, vấn đề phức tạp hơn nhiều: một service chậm có thể gây cascading failure qua toàn bộ call chain, connection pool exhaustion ở một node ảnh hưởng đến hàng chục service downstream.
graph LR
A[Client
10K req/s] --> B[API Gateway]
B --> C[Auth Service]
B --> D[Product Service]
B --> E[Order Service]
D --> F[(Database)]
E --> F
E --> G[Payment Service]
G --> H[External Payment API]
style A fill:#e94560,stroke:#fff,color:#fff
style B fill:#2c3e50,stroke:#fff,color:#fff
style C fill:#f8f9fa,stroke:#e94560,color:#2c3e50
style D fill:#f8f9fa,stroke:#e94560,color:#2c3e50
style E fill:#f8f9fa,stroke:#e94560,color:#2c3e50
style F fill:#16213e,stroke:#fff,color:#fff
style G fill:#f8f9fa,stroke:#e94560,color:#2c3e50
style H fill:#ff9800,stroke:#fff,color:#fff
Kiến trúc microservices điển hình — mỗi node đều có thể là bottleneck dưới tải cao
Load testing cho hệ thống phân tán cần trả lời được những câu hỏi sau:
- Throughput tối đa — Hệ thống xử lý được bao nhiêu request/giây trước khi response time vượt ngưỡng chấp nhận?
- Điểm gãy (breaking point) — Ở mức tải nào hệ thống bắt đầu trả lỗi hoặc timeout?
- Cascading failure — Khi một service bị quá tải, ảnh hưởng lan rộng đến đâu?
- Resource saturation — CPU, memory, connection pool cạn kiệt ở service nào trước?
- Recovery time — Sau khi tải giảm, hệ thống mất bao lâu để phục hồi về trạng thái bình thường?
2. Các loại kiểm thử hiệu năng
Mỗi loại kiểm thử phục vụ một mục đích khác nhau. Hiểu rõ từng loại giúp bạn thiết kế test suite phù hợp với yêu cầu SLA của hệ thống.
graph TB
subgraph Types["Các loại Performance Testing"]
A["🔥 Smoke Test
Kiểm tra cơ bản
1-5 VUs"]
B["📊 Load Test
Tải trung bình
Target VUs"]
C["💪 Stress Test
Vượt ngưỡng
2-3x Target"]
D["⚡ Spike Test
Tăng đột biến
0 → Max → 0"]
E["🕐 Soak Test
Chạy dài hạn
4-24 giờ"]
F["🎯 Breakpoint Test
Tìm giới hạn
Ramp liên tục"]
end
style A fill:#4CAF50,stroke:#fff,color:#fff
style B fill:#2196F3,stroke:#fff,color:#fff
style C fill:#ff9800,stroke:#fff,color:#fff
style D fill:#e94560,stroke:#fff,color:#fff
style E fill:#9C27B0,stroke:#fff,color:#fff
style F fill:#16213e,stroke:#fff,color:#fff
6 loại performance testing chính — từ kiểm tra cơ bản đến tìm điểm gãy hệ thống
| Loại test | Mục tiêu | Mô hình tải | Thời gian | Khi nào dùng |
|---|---|---|---|---|
| Smoke Test | Verify script chạy đúng | 1-5 VUs cố định | 1-3 phút | Sau mỗi thay đổi test script |
| Load Test | Đánh giá hiệu năng bình thường | Ramp up → steady → ramp down | 10-30 phút | Mỗi sprint/release |
| Stress Test | Tìm giới hạn hệ thống | Tải vượt 2-3x bình thường | 15-30 phút | Trước launch lớn |
| Spike Test | Kiểm tra phản ứng với tải đột biến | 0 → peak → 0 trong vài giây | 5-10 phút | Flash sale, viral event |
| Soak Test | Phát hiện memory leak, connection leak | Tải trung bình kéo dài | 4-24 giờ | Trước production release |
| Breakpoint Test | Xác định maximum capacity | Ramp liên tục đến khi fail | Không xác định | Capacity planning |
💡 Thứ tự kiểm thử khuyến nghị
Luôn bắt đầu với Smoke Test để đảm bảo script hoạt động đúng, sau đó Load Test để có baseline, rồi mới chuyển sang Stress/Spike/Soak cho các kịch bản nâng cao. Đừng nhảy thẳng vào stress test — bạn sẽ tốn thời gian debug test script thay vì debug hệ thống.
3. Grafana k6 — Load testing hiện đại với JavaScript/TypeScript
3.1 Tại sao chọn k6?
k6 là công cụ load testing mã nguồn mở từ Grafana Labs, viết bằng Go nhưng cho phép scripting bằng JavaScript/TypeScript. Điểm mạnh lớn nhất của k6 là engine hiệu năng cao — sử dụng ít CPU hơn 70% so với các tool tương tự, cho phép tạo hàng nghìn virtual users trên một máy.
Từ phiên bản 1.0, k6 hỗ trợ TypeScript native — bạn có type safety, IDE autocomplete mà không cần build step riêng.
3.2 Viết test script đầu tiên
// load-test.ts — k6 load test cho API endpoint
import http from 'k6/http';
import { check, sleep } from 'k6';
import { Rate, Trend } from 'k6/metrics';
// Custom metrics
const errorRate = new Rate('errors');
const responseTime = new Trend('response_time_ms');
// Test configuration
export const options = {
scenarios: {
average_load: {
executor: 'ramping-vus',
startVUs: 0,
stages: [
{ duration: '2m', target: 100 }, // Ramp up to 100 VUs
{ duration: '5m', target: 100 }, // Stay at 100 VUs
{ duration: '2m', target: 0 }, // Ramp down
],
},
},
thresholds: {
http_req_duration: ['p(95)<500', 'p(99)<1000'],
errors: ['rate<0.01'], // Error rate < 1%
response_time_ms: ['p(95)<400'],
},
};
export default function () {
const res = http.get('https://api.example.com/products', {
headers: { 'Authorization': `Bearer ${__ENV.API_TOKEN}` },
tags: { name: 'GetProducts' },
});
check(res, {
'status is 200': (r) => r.status === 200,
'response time < 500ms': (r) => r.timings.duration < 500,
'body has products': (r) => JSON.parse(r.body).length > 0,
});
errorRate.add(res.status !== 200);
responseTime.add(res.timings.duration);
sleep(1); // Think time giữa các request
}
3.3 Scenarios và Executors nâng cao
k6 cung cấp nhiều loại executor cho các mô hình tải khác nhau:
export const options = {
scenarios: {
// Scenario 1: Constant arrival rate — kiểm soát RPS
constant_rps: {
executor: 'constant-arrival-rate',
rate: 200, // 200 iterations/giây
timeUnit: '1s',
duration: '5m',
preAllocatedVUs: 50,
maxVUs: 200,
},
// Scenario 2: Spike test
spike: {
executor: 'ramping-vus',
startVUs: 0,
stages: [
{ duration: '10s', target: 0 },
{ duration: '10s', target: 500 }, // Spike lên 500
{ duration: '30s', target: 500 },
{ duration: '10s', target: 0 }, // Drop về 0
],
startTime: '6m', // Bắt đầu sau scenario 1
},
},
};
Constant VUs vs Constant Arrival Rate
Constant VUs (ramping-vus): mỗi VU hoàn thành 1 iteration rồi bắt đầu iteration mới. RPS phụ thuộc vào response time — server chậm → RPS giảm. Dùng khi muốn mô phỏng số lượng người dùng đồng thời.
Constant Arrival Rate: đảm bảo chính xác N iterations/giây bất kể response time. k6 sẽ spawn thêm VUs nếu cần. Dùng khi muốn đo hệ thống dưới throughput cố định — phù hợp cho SLA testing.
3.4 Browser testing với k6
k6 tích hợp browser API tương thích Playwright, cho phép chạy hybrid test — kết hợp protocol-level (HTTP) và browser-level trong cùng một kịch bản:
import { browser } from 'k6/browser';
import http from 'k6/http';
export const options = {
scenarios: {
browser_test: {
executor: 'constant-vus',
vus: 5,
duration: '3m',
options: { browser: { type: 'chromium' } },
},
api_test: {
executor: 'constant-arrival-rate',
rate: 100,
timeUnit: '1s',
duration: '3m',
preAllocatedVUs: 20,
},
},
};
export async function browser_test() {
const page = await browser.newPage();
await page.goto('https://app.example.com/dashboard');
// Đo Web Vitals thực tế
const lcp = await page.evaluate(() => {
return new Promise(resolve => {
new PerformanceObserver(list => {
const entries = list.getEntries();
resolve(entries[entries.length - 1].startTime);
}).observe({ type: 'largest-contentful-paint', buffered: true });
});
});
console.log(`LCP: ${lcp}ms`);
await page.close();
}
4. NBomber — Load testing thuần .NET cho C# developer
4.1 Khi nào chọn NBomber?
Nếu team bạn chủ yếu sử dụng .NET và muốn viết load test bằng C#/F# — tận dụng IDE, debug, NuGet packages — thì NBomber là lựa chọn lý tưởng. NBomber hoạt động như một thư viện .NET, cài qua NuGet, và test scenario chạy như unit test thông thường.
4.2 Viết scenario với NBomber
using NBomber.CSharp;
using NBomber.Http.CSharp;
var httpClient = new HttpClient();
var scenario = Scenario.Create("get_products", async context =>
{
var request = Http.CreateRequest("GET", "https://api.example.com/products")
.WithHeader("Authorization", "Bearer " + Environment.GetEnvironmentVariable("API_TOKEN"));
var response = await Http.Send(httpClient, request);
return response;
})
.WithLoadSimulations(
Simulation.RampingInject(rate: 100, interval: TimeSpan.FromSeconds(1),
during: TimeSpan.FromMinutes(2)),
Simulation.Inject(rate: 100, interval: TimeSpan.FromSeconds(1),
during: TimeSpan.FromMinutes(5)),
Simulation.RampingInject(rate: 0, interval: TimeSpan.FromSeconds(1),
during: TimeSpan.FromMinutes(2))
);
NBomberRunner
.RegisterScenarios(scenario)
.WithReportFormats(ReportFormat.Html, ReportFormat.Csv)
.WithReportFolder("./reports")
.Run();
4.3 Test đa protocol
Sức mạnh của NBomber là khả năng test bất kỳ protocol nào — HTTP, gRPC, WebSocket, database, message queue — trong cùng một scenario:
var httpStep = Scenario.Create("mixed_workload", async context =>
{
// Step 1: Gọi REST API
var apiResponse = await Http.Send(httpClient,
Http.CreateRequest("GET", "https://api.example.com/products"));
if (apiResponse.StatusCode != "200")
return Response.Fail();
// Step 2: Gọi gRPC service
var grpcResponse = await grpcClient.GetProductDetailsAsync(
new ProductRequest { Id = context.ScenarioInfo.ThreadNumber });
// Step 3: Publish message to RabbitMQ
channel.BasicPublish(exchange: "", routingKey: "orders",
body: Encoding.UTF8.GetBytes($"order-{context.InvocationNumber}"));
return Response.Ok(statusCode: "200",
sizeBytes: apiResponse.SizeBytes + grpcResponse.CalculateSize());
})
.WithLoadSimulations(
Simulation.KeepConstant(copies: 50, during: TimeSpan.FromMinutes(10))
);
5. So sánh k6 vs NBomber
| Tiêu chí | Grafana k6 | NBomber |
|---|---|---|
| Ngôn ngữ | JavaScript / TypeScript | C# / F# |
| Engine | Go (goroutines) | .NET (async/await) |
| Cài đặt | Binary standalone | NuGet package |
| IDE support | VS Code + extensions | Visual Studio / Rider (full debug) |
| Browser testing | Có (Chromium built-in) | Có (qua Playwright NuGet) |
| Protocol | HTTP, WebSocket, gRPC (extension) | Bất kỳ (HTTP, gRPC, WS, DB, MQ...) |
| Distributed testing | k6 Cloud hoặc k6-operator (K8s) | NBomber Cluster |
| Reporting | Grafana dashboards, JSON, CSV | HTML, CSV, TXT, Markdown |
| CI/CD | Native (exit code dựa trên thresholds) | xUnit/NUnit runner, threshold assertions |
| Giá | OSS miễn phí, Cloud trả phí | Free (personal), $99/tháng (business) |
| Phù hợp | Team đa ngôn ngữ, DevOps-driven | Team .NET, developer-driven testing |
💡 Khuyến nghị thực tế
Nếu team bạn chủ yếu dùng .NET và muốn load test chạy như unit test trong CI pipeline → chọn NBomber. Nếu team đa ngôn ngữ hoặc ưu tiên scripting nhanh với TypeScript → chọn k6. Nhiều tổ chức lớn sử dụng cả hai: k6 cho API-level testing nhanh trong CI, NBomber cho integration testing phức tạp cần debug.
6. Chiến lược Load Testing cho Microservices
6.1 Mô hình kiểm thử phân tầng
graph TB
subgraph L1["Tầng 1 — Component Testing"]
A["Test từng service
riêng lẻ"]
B["Mock dependencies"]
C["Đo baseline
response time"]
end
subgraph L2["Tầng 2 — Integration Testing"]
D["Test chuỗi 2-3
services"]
E["Real dependencies"]
F["Đo latency
end-to-end"]
end
subgraph L3["Tầng 3 — System Testing"]
G["Test toàn bộ
hệ thống"]
H["Production-like
traffic"]
I["Đo throughput
& error rate"]
end
L1 --> L2 --> L3
style A fill:#4CAF50,stroke:#fff,color:#fff
style B fill:#4CAF50,stroke:#fff,color:#fff
style C fill:#4CAF50,stroke:#fff,color:#fff
style D fill:#2196F3,stroke:#fff,color:#fff
style E fill:#2196F3,stroke:#fff,color:#fff
style F fill:#2196F3,stroke:#fff,color:#fff
style G fill:#e94560,stroke:#fff,color:#fff
style H fill:#e94560,stroke:#fff,color:#fff
style I fill:#e94560,stroke:#fff,color:#fff
Mô hình 3 tầng kiểm thử hiệu năng cho microservices
6.2 Thiết kế test scenario sát thực tế
Sai lầm phổ biến nhất khi load testing là tạo traffic pattern không giống thực tế. Production traffic hiếm khi đồng đều — thường có hot paths (20% endpoints nhận 80% traffic) và cold paths.
// k6: Mô phỏng traffic pattern thực tế
export const options = {
scenarios: {
// 70% traffic: Browse products (đọc)
browse: {
executor: 'constant-arrival-rate',
rate: 700,
timeUnit: '1s',
duration: '10m',
preAllocatedVUs: 100,
exec: 'browseProducts',
},
// 20% traffic: Search (đọc + tính toán)
search: {
executor: 'constant-arrival-rate',
rate: 200,
timeUnit: '1s',
duration: '10m',
preAllocatedVUs: 50,
exec: 'searchProducts',
},
// 10% traffic: Checkout (ghi)
checkout: {
executor: 'constant-arrival-rate',
rate: 100,
timeUnit: '1s',
duration: '10m',
preAllocatedVUs: 30,
exec: 'checkout',
},
},
};
export function browseProducts() {
const categoryId = Math.floor(Math.random() * 20) + 1;
http.get(`${BASE_URL}/products?category=${categoryId}`);
sleep(Math.random() * 3 + 1); // Think time 1-4s
}
export function searchProducts() {
const terms = ['laptop', 'phone', 'tablet', 'headphone', 'camera'];
const q = terms[Math.floor(Math.random() * terms.length)];
http.get(`${BASE_URL}/search?q=${q}`);
sleep(Math.random() * 2 + 0.5);
}
export function checkout() {
const payload = JSON.stringify({
productId: Math.floor(Math.random() * 1000) + 1,
quantity: Math.floor(Math.random() * 3) + 1,
});
http.post(`${BASE_URL}/orders`, payload, {
headers: { 'Content-Type': 'application/json' },
});
sleep(Math.random() * 5 + 2); // Think time dài hơn cho checkout
}
6.3 Thresholds và SLA validation
Thresholds chính là cách biến load test từ "báo cáo" thành "quality gate" — nếu metric vượt ngưỡng, test fail và CI pipeline dừng lại.
export const options = {
thresholds: {
// Global thresholds
http_req_duration: [
'p(50)<200', // 50th percentile < 200ms
'p(95)<500', // 95th percentile < 500ms
'p(99)<1000', // 99th percentile < 1s
],
http_req_failed: ['rate<0.01'], // <1% lỗi
// Per-endpoint thresholds
'http_req_duration{name:GetProducts}': ['p(95)<300'],
'http_req_duration{name:Checkout}': ['p(95)<800'],
'http_req_duration{name:Search}': ['p(95)<600'],
// Custom metrics
'errors{scenario:checkout}': ['rate<0.005'], // Checkout: <0.5% lỗi
},
};
7. Tích hợp Load Testing vào CI/CD Pipeline
graph LR
A[Code Push] --> B[Build & Unit Test]
B --> C[Deploy to Staging]
C --> D[Smoke Test
k6 / NBomber]
D -->|Pass| E[Load Test
10 min]
D -->|Fail| F[Alert & Stop]
E -->|Thresholds OK| G[Deploy to Prod]
E -->|Thresholds Fail| F
G --> H[Canary Test
5% traffic]
H -->|Healthy| I[Full Rollout]
H -->|Degraded| J[Rollback]
style A fill:#f8f9fa,stroke:#e94560,color:#2c3e50
style D fill:#4CAF50,stroke:#fff,color:#fff
style E fill:#2196F3,stroke:#fff,color:#fff
style F fill:#e94560,stroke:#fff,color:#fff
style G fill:#4CAF50,stroke:#fff,color:#fff
style H fill:#ff9800,stroke:#fff,color:#fff
style I fill:#4CAF50,stroke:#fff,color:#fff
style J fill:#e94560,stroke:#fff,color:#fff
Load testing tích hợp CI/CD — smoke test gate trước, load test gate sau
7.1 GitHub Actions + k6
# .github/workflows/load-test.yml
name: Load Test
on:
pull_request:
branches: [main]
jobs:
load-test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Deploy to staging
run: ./deploy-staging.sh
- name: Install k6
run: |
sudo gpg -k
sudo gpg --no-default-keyring --keyring /usr/share/keyrings/k6-archive-keyring.gpg \
--keyserver hkp://keyserver.ubuntu.com:80 --recv-keys C5AD17C747E3415A3642D57D77C6C491D6AC1D68
echo "deb [signed-by=/usr/share/keyrings/k6-archive-keyring.gpg] https://dl.k6.io/deb stable main" \
| sudo tee /etc/apt/sources.list.d/k6.list
sudo apt-get update && sudo apt-get install k6
- name: Run smoke test
run: k6 run --tag testid=smoke tests/smoke.ts
env:
API_TOKEN: ${{ secrets.STAGING_API_TOKEN }}
- name: Run load test
run: k6 run tests/load.ts
env:
API_TOKEN: ${{ secrets.STAGING_API_TOKEN }}
- name: Upload results
if: always()
uses: actions/upload-artifact@v4
with:
name: k6-results
path: results/
7.2 .NET CI + NBomber
// LoadTests/ProductApiLoadTest.cs — chạy như xUnit test
public class ProductApiLoadTest
{
[Fact]
public void Products_endpoint_handles_100rps()
{
var httpClient = new HttpClient();
var scenario = Scenario.Create("get_products", async context =>
{
var response = await Http.Send(httpClient,
Http.CreateRequest("GET", "https://staging.api.example.com/products"));
return response;
})
.WithLoadSimulations(
Simulation.Inject(rate: 100, interval: TimeSpan.FromSeconds(1),
during: TimeSpan.FromMinutes(5))
);
var stats = NBomberRunner
.RegisterScenarios(scenario)
.Run();
var scnStats = stats.ScenarioStats[0];
// Assert SLA
Assert.True(scnStats.Ok.Latency.Percent95 < 500,
$"P95 latency {scnStats.Ok.Latency.Percent95}ms exceeds 500ms threshold");
Assert.True(scnStats.Fail.Request.Percent < 1,
$"Error rate {scnStats.Fail.Request.Percent}% exceeds 1% threshold");
}
}
8. Phân tích kết quả và debug bottleneck
8.1 Các metric quan trọng cần theo dõi
| Metric | Ý nghĩa | Ngưỡng đề xuất | Khi vượt ngưỡng |
|---|---|---|---|
| p50 (median) | Response time trung vị | < 200ms | Hệ thống chậm toàn diện |
| p95 | 95% request nhanh hơn giá trị này | < 500ms | Tail latency ảnh hưởng UX |
| p99 | 99% request nhanh hơn giá trị này | < 1000ms | Outlier do GC, cold start, DB lock |
| Error rate | % request lỗi (4xx, 5xx, timeout) | < 1% | Service overloaded hoặc bug |
| Throughput (RPS) | Request per second thực tế | ≥ target SLA | Bottleneck ở compute hoặc I/O |
| VU active | Số virtual users đang hoạt động | Theo kế hoạch | VU stuck = connection leak |
8.2 Pattern phát hiện bottleneck
⚠️ Những dấu hiệu phổ biến
Response time tăng tuyến tính theo VU → CPU-bound: application đang serialize xử lý, cần review async code hoặc scale horizontal.
Response time tăng đột ngột tại ngưỡng N VUs → Connection pool exhaustion: database hoặc downstream service hết connection. Kiểm tra MaxPoolSize, HttpClient lifecycle.
Error rate spike kèm response time giảm → Circuit breaker đang mở hoặc request bị reject sớm. Tốt cho hệ thống (self-protection) nhưng cần tune ngưỡng.
Memory tăng dần trong soak test → Memory leak: thường do event handler không unsubscribe, HttpClient tạo mới liên tục, hoặc cache không có eviction policy.
9. Best Practices cho Production Load Testing
9.1 Quy tắc vàng
- Test trên môi trường giống production — Cùng hardware spec, cùng data volume, cùng network topology. Test trên laptop với 100 rows trong DB không nói lên điều gì.
- Sử dụng dữ liệu thực tế — Tạo dataset phản ánh phân bố thực: product catalog đầy đủ, user profiles đa dạng, search queries từ access log thật.
- Đo từ phía client, không phải server — Server metrics cho thấy response time xử lý, nhưng user trải nghiệm bao gồm cả network latency, DNS resolution, TLS handshake.
- Warm up trước khi đo — JIT compilation (.NET), connection pool initialization, cache warming đều ảnh hưởng đến kết quả nếu đo ngay từ request đầu tiên.
- Chạy nhiều lần, lấy trung bình — Kết quả một lần chạy có variance cao. Chạy ít nhất 3 lần với cùng config, loại bỏ outlier, báo cáo kết quả trung bình.
9.2 Anti-patterns cần tránh
- Test không có think time — User thật không bắn request liên tục. Thêm
sleep(1-5s)giữa các request để mô phỏng reading time. - Hardcode test data — Gọi cùng một API endpoint với cùng parameter → cache hit 100% → kết quả không phản ánh thực tế.
- Bỏ qua ramp-up period — Bắn 10.000 VUs cùng lúc tạo thundering herd — không phải load test mà là DDoS. Luôn ramp up dần.
- Chỉ test happy path — Production có 404, 401, timeout, malformed request. Test script nên bao gồm error scenarios.
- Không monitor resource phía server — Load test chỉ cho output metrics. Cần kết hợp APM (Application Performance Monitoring) để biết CPU/memory/disk I/O phía server đang ở mức nào.
10. Kết luận
Load testing không phải là hoạt động chạy một lần trước launch rồi quên đi. Trong hệ thống phân tán, mỗi lần thêm service mới, thay đổi schema database, hoặc upgrade dependency đều có thể ảnh hưởng đến hiệu năng tổng thể. Tích hợp load testing vào CI/CD pipeline — bắt đầu với smoke test gate, tiến tới load test gate — là cách duy nhất để đảm bảo performance regression được phát hiện sớm.
Dù bạn chọn k6 cho sự linh hoạt của JavaScript/TypeScript hay NBomber cho sức mạnh của .NET ecosystem, điều quan trọng nhất là bắt đầu — một smoke test đơn giản trong CI pipeline còn có giá trị hơn một kế hoạch load testing hoàn hảo mà không bao giờ được thực hiện.
Tham khảo
- Grafana k6 Documentation — Official Docs
- NBomber — Distributed load testing framework for .NET
- k6.io — Load testing for engineering teams
- Grafana Cloud k6 — Performance & Load Testing
- k6 GitHub Repository — Open Source
- NBomber GitHub Repository — .NET Load Testing
- Best API Load Testing Tools 2026 — PFLB Comparison
AWS Step Functions: Điều Phối Workflow Serverless Cho Hệ Thống Phân Tán
Chiến lược Caching đa tầng: Từ Browser đến Database cho ứng dụng hiệu năng cao
Disclaimer: The opinions expressed in this blog are solely my own and do not reflect the views or opinions of my employer or any affiliated organizations. The content provided is for informational and educational purposes only and should not be taken as professional advice. While I strive to provide accurate and up-to-date information, I make no warranties or guarantees about the completeness, reliability, or accuracy of the content. Readers are encouraged to verify the information and seek independent advice as needed. I disclaim any liability for decisions or actions taken based on the content of this blog.