High Concurrency Testing Guide: JMeter, Locust, k6 Tool Comparison & Tutorial | 2025

High Concurrency Testing Guide: JMeter, Locust, k6 Tool Comparison & Tutorial
Introduction: Going Live Without Testing is Gambling
"Did you test before going live?" "Yes, all features work." "Load testing?" "..."
Then on the big sale day, the system crashed.
Functional testing only ensures "it works"—load testing ensures "it can handle the load." Going live with a high concurrency system without load testing is like driving blindfolded.
This article will take you from "why load test" through comparing four major tools, teaching you to write test scripts, interpret metrics, and finally how to integrate with CI/CD for continuous load testing.
If you're not familiar with high concurrency basics, we recommend first reading What is High Concurrency? Complete Guide.
1. Why Load Testing is Needed
1.1 Find System Bottlenecks
Load testing exposes system weaknesses:
- Database connections insufficient
- Certain API particularly slow
- Memory leaks
- Network bandwidth bottleneck
These problems don't appear at low traffic—only load testing can find them.
1.2 Validate Architecture Design
How much QPS can your architecture design handle? Theoretical calculations aren't accurate—real testing tells you.
- Is read-write separation effective?
- Is cache hit rate high enough?
- Is load balancing distributing evenly?
Load testing is the validation method for architecture design. For detailed architecture design principles, see High Concurrency Architecture Design.
1.3 Capacity Planning Basis
Big sale expects 1 million users online simultaneously. How many machines are needed?
Without load test data, you can only guess. With load test data, you can plan precisely.
1.4 Establish Baseline
Load testing isn't a one-time thing. Every new version release might change performance.
Regular load testing establishes performance baseline. If new version performance drops, you'll know immediately.
2. Load Testing Tool Comparison
There are many load testing tools on the market. Here are four most commonly used.
2.1 Apache JMeter
Introduction: Veteran load testing tool written in Java, most comprehensive features.
Pros:
- GUI interface, easy to start
- Supports multiple protocols (HTTP, JDBC, LDAP, JMS)
- Rich plugin ecosystem
- Distributed testing support
Cons:
- Written in Java, higher resource usage
- GUI lags during large tests
- Scripts are XML, hard to maintain
Suitable for:
- Need to test multiple protocols
- Team unfamiliar with programming languages
- Enterprise applications
2.2 Locust
Introduction: Modern load testing tool written in Python.
Pros:
- Python scripts, high flexibility
- Lightweight, low resource usage
- Native distributed architecture support
- Web UI real-time monitoring
Cons:
- Only supports HTTP
- Python syntax has learning curve
- Slightly weaker for extreme performance scenarios
Suitable for:
- Python developers
- Web API testing
- Complex test logic needed
2.3 k6
Introduction: Modern load testing tool using JavaScript scripts, maintained by Grafana.
Pros:
- Written in Go, excellent performance
- JavaScript/TypeScript for scripts
- Great developer experience (CLI first)
- Cloud service integration (Grafana Cloud)
Cons:
- Only supports HTTP/WebSocket/gRPC
- Relatively new, smaller community
- Advanced features need paid version
Suitable for:
- Frontend/fullstack developers
- DevOps teams
- Need CI/CD integration
2.4 wrk
Introduction: Minimalist load testing tool written in C.
Pros:
- Extremely lightweight, lowest resource usage
- Single machine can generate extremely high QPS
- Lua script extension
Cons:
- Simple features, only tests HTTP
- No GUI
- Weak reporting features
Suitable for:
- Quick benchmark tests
- Scenarios requiring extreme performance
- Simple API testing
Tool Comparison Table
| Tool | Language | Scripts | GUI | Distributed | Resource Usage | Use Cases |
|---|---|---|---|---|---|---|
| JMeter | Java | XML | Yes | Supported | High | Enterprise, multi-protocol |
| Locust | Python | Python | Web | Native | Low | Python teams, Web API |
| k6 | Go | JavaScript | No | Paid version | Low | DevOps, CI/CD |
| wrk | C | Lua | No | No | Very Low | Quick benchmarks |
3. Understanding Test Metrics
After running load tests, you see lots of numbers. What do they mean?
3.1 Throughput Metrics
QPS (Queries Per Second)
Queries per second. For Web APIs, this is the most common throughput metric.
- 100 QPS: Small website
- 1,000 QPS: Medium website
- 10,000+ QPS: Large website
TPS (Transactions Per Second)
Transactions per second. One transaction might include multiple queries.
Example: One purchase = check product + deduct inventory + create order + charge payment = 1 TPS, but might be 4 QPS.
RPS (Requests Per Second)
Requests per second. Usually synonymous with QPS.
3.2 Response Time Metrics
Average Response Time (Avg)
Average of all request response times. Looks intuitive but easily affected by extreme values.
P50 / P95 / P99
Percentiles, better reflect real experience.
- P50: 50% of requests complete within this time (median)
- P95: 95% of requests complete within this time
- P99: 99% of requests complete within this time
Why care about P99? Because 1% of slow requests might affect lots of users.
Example:
- Average response time: 100ms
- P50: 80ms
- P95: 200ms
- P99: 1000ms
This means although average looks okay, 1% of users wait over 1 second. That 1% might be your VIP customers.
3.3 Error Metrics
Error Rate
Proportion of failed requests. Includes HTTP 5xx errors, timeouts, connection failures.
- < 0.1%: Excellent
- 0.1% - 1%: Acceptable
-
1%: Needs attention
Timeout Rate
Proportion of timed-out requests. Usually set 30 or 60 second timeout.
3.4 Resource Metrics
During load testing, also monitor server resources:
- CPU usage: Over 80% needs attention
- Memory usage: Watch for continuous increase (leak)
- Network I/O: Is bandwidth maxed
- Disk I/O: Is there heavy read/write
These metrics help locate where bottlenecks are.
4. Load Test Script Tutorial
Talk without practice is fake. Here are actual examples for three tools.
4.1 JMeter Example
JMeter mainly uses GUI to design test plans, core components:
- Thread Group: Set concurrent user count
- HTTP Request: Define API to test
- Listener: Collect and display results
<ThreadGroup>
<numThreads>100</numThreads>
<rampTime>10</rampTime>
<duration>60</duration>
</ThreadGroup>
<HTTPSampler>
<domain>api.example.com</domain>
<path>/products</path>
<method>GET</method>
</HTTPSampler>
Actual operation flow:
- Open JMeter GUI
- Add Thread Group, set 100 users, 10 second ramp-up, run 60 seconds
- Add HTTP Request, fill in API info
- Add Summary Report to view results
- Click run
4.2 Locust Example
Locust uses Python for scripts, very flexible.
# locustfile.py
from locust import HttpUser, task, between
class WebUser(HttpUser):
# Wait 1-3 seconds between requests
wait_time = between(1, 3)
@task(3) # Weight 3, higher execution frequency
def get_products(self):
self.client.get("/products")
@task(1) # Weight 1
def get_product_detail(self):
product_id = 123
self.client.get(f"/products/{product_id}")
@task(1)
def create_order(self):
self.client.post("/orders", json={
"product_id": 123,
"quantity": 1
})
def on_start(self):
# Runs when each user starts (e.g., login)
self.client.post("/login", json={
"username": "test",
"password": "test123"
})
Run commands:
# Start Locust Web UI
locust -f locustfile.py --host=https://api.example.com
# Or run directly from command line
locust -f locustfile.py --host=https://api.example.com \
--users 100 --spawn-rate 10 --run-time 60s --headless
To learn more about Python handling high concurrency, see Python vs Golang High Concurrency.
4.3 k6 Example
k6 uses JavaScript for scripts, friendly for frontend developers.
// script.js
import http from 'k6/http';
import { check, sleep } from 'k6';
// Test configuration
export const options = {
stages: [
{ duration: '10s', target: 50 }, // Ramp up to 50 users in 10s
{ duration: '30s', target: 100 }, // Maintain 100 users for 30s
{ duration: '10s', target: 0 }, // Ramp down to 0 in 10s
],
thresholds: {
http_req_duration: ['p(95)<500'], // 95% requests under 500ms
http_req_failed: ['rate<0.01'], // Error rate < 1%
},
};
export default function () {
// Get product list
const productsRes = http.get('https://api.example.com/products');
check(productsRes, {
'products status is 200': (r) => r.status === 200,
});
sleep(1);
// Get product detail
const detailRes = http.get('https://api.example.com/products/123');
check(detailRes, {
'detail status is 200': (r) => r.status === 200,
'detail has name': (r) => r.json('name') !== undefined,
});
sleep(1);
// Create order
const orderRes = http.post(
'https://api.example.com/orders',
JSON.stringify({ product_id: 123, quantity: 1 }),
{ headers: { 'Content-Type': 'application/json' } }
);
check(orderRes, {
'order created': (r) => r.status === 201,
});
sleep(1);
}
Run command:
k6 run script.js
5. CI/CD Integration for Continuous Load Testing
Load testing shouldn't be a one-time thing. Integrate with CI/CD, run automatically with every release.
5.1 Why Continuous Load Testing
- New code might introduce performance issues
- Catch early, avoid problems after going live
- Establish performance baseline, track trends
- Prevent performance regression
5.2 Integration Methods
GitHub Actions + k6 Example
# .github/workflows/load-test.yml
name: Load Test
on:
push:
branches: [main]
pull_request:
branches: [main]
jobs:
load-test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- 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 C5AD17C747E3415A3642D57D77C6C491D6AC1D69
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 load test
run: k6 run --out json=results.json tests/load-test.js
- name: Check thresholds
run: |
if grep -q '"thresholds":{"http_req_duration":\[{"ok":false' results.json; then
echo "Performance threshold failed!"
exit 1
fi
5.3 Threshold Settings
Set clear thresholds to automatically determine pass/fail:
// k6 thresholds
export const options = {
thresholds: {
http_req_duration: ['p(95)<500', 'p(99)<1000'],
http_req_failed: ['rate<0.01'],
http_reqs: ['rate>100'], // At least 100 QPS
},
};
6. Capacity Planning Methods
How to use load test data for capacity planning?
6.1 Estimation Formula
Step 1: Estimate Expected Traffic
Expected QPS = Daily Active Users × Requests Per User ÷ Active Seconds
Example:
- Daily active users: 100,000
- Requests per user: 50
- Active time: 8 hours = 28,800 seconds
- Expected QPS = 100,000 × 50 ÷ 28,800 ≈ 174
Step 2: Consider Peak
Peak is usually 2-5x average.
Peak QPS = Expected QPS × Peak Factor
Assuming peak is 3x average:
- Peak QPS = 174 × 3 ≈ 522
6.2 Safety Factor
Don't run systems to their limits. Leave room for unexpected situations.
Target Capacity = Peak QPS × Safety Factor
Safety factor usually 1.5-2x:
- Target Capacity = 522 × 1.5 ≈ 783 QPS
6.3 Scaling Plan
Knowing target capacity, use load test data to calculate needed resources.
Assuming load test results:
- Single 4C8G machine can handle 200 QPS
- Target capacity 783 QPS
- Machines needed = 783 ÷ 200 ≈ 4
Considering high availability (N+1 redundancy), need at least 5 machines.
Unsure how much traffic your system can handle? Book a load testing consultation and let experts help plan your capacity.
7. Common Problem Troubleshooting
What to do when you encounter problems during load testing?
7.1 Test Client Bottleneck
Symptoms: Test machine CPU spikes, but server is idle.
Cause: Load testing tool itself becomes bottleneck. JMeter has high resource usage, limited QPS from single machine.
Solutions:
- Switch to lightweight tools (k6, wrk)
- Distributed load testing (multiple test machines)
- Disable unnecessary monitoring plugins
7.2 Network Bottleneck
Symptoms: Network traffic maxed, but CPU/memory are fine.
Cause: Insufficient bandwidth, or network equipment becomes bottleneck.
Solutions:
- Check bandwidth between test machine and server
- Put test machine in same internal network
- Use cloud load testing services
7.3 Application Bottleneck
Symptoms: Application CPU spikes, response slows.
Cause: Code performance issues, resource contention, thread exhaustion.
Troubleshooting directions:
- Profiling to find hot functions
- Check for lock contention
- Check thread pool/connection pool settings
7.4 Resource Leaks
Symptoms: Performance drops after running a while, memory continuously rising.
Cause: Memory leak, connection leak, file descriptor leak.
Troubleshooting directions:
- Use APM tools to track memory usage
- Check if connections are properly closed
- Look at system's open file count
FAQ
Q1: Should load testing be done in production or test environment?
Test environment for initial validation, production for final confirmation. But production load testing needs care—recommend doing during off-peak hours with degradation plans in place.
Q2: How long should load tests run?
At least 5-10 minutes. Too short might miss memory leaks and other issues. Stability tests can run hours or even a day.
Q3: How to simulate real user behavior?
Not all users send requests at the same second. Use ramp-up to simulate users gradually joining, think time to simulate user thinking, different weights to simulate different behavior proportions.
Q4: How to do distributed load testing?
JMeter has remote testing, Locust natively supports distributed, k6 paid version has Cloud features. Or manually run on multiple machines and merge results.
Q5: What if load test results are unstable?
Run multiple times and take average. Eliminate environmental interference (other programs, network fluctuations). Ensure starting state is consistent for each test (restart services, clear cache).
Conclusion: Load Testing is a Must for High Concurrency Systems
A system without load testing is like a soldier without battle experience.
Key Takeaways:
- Load testing finds bottlenecks, validates architecture, plans capacity
- JMeter is full-featured, Locust Python-friendly, k6 modern, wrk minimalist
- Focus on P99 response time, not just average
- Error rate and resource metrics are equally important
- Integrate CI/CD for continuous load testing
- Capacity planning should consider peak and safety factors
Extended Reading:
- What is High Concurrency? Complete Guide
- High Concurrency Architecture Design
- High Concurrency Database Design
- Python vs Golang High Concurrency
- High Concurrency Transaction System Design
- Cloud High Concurrency Architecture
Need a Second Opinion on Architecture?
Good load testing can prevent disasters. If you're:
- Preparing for a big sale, worried system can't handle it
- Want to know where your system's performance limits are
- Need to plan capacity and budget
Book an architecture consultation and let's review your system performance together.
All consultation content is completely confidential, no sales pressure.
References
- Apache JMeter Official Documentation (2024)
- Locust Official Documentation (2024)
- k6 Official Documentation (2024)
- Google, "Site Reliability Engineering" (2016)
- AWS, "Performance Testing Best Practices" (2024)
Need Professional Cloud Advice?
Whether you're evaluating cloud platforms, optimizing existing architecture, or looking for cost-saving solutions, we can help
Book Free ConsultationRelated Articles
DDoS Testing Guide: How to Legally Test Your Website's DDoS Defense Capabilities (2025)
Complete DDoS testing guide teaching you how to legally test your website's DDoS defense capabilities. Introduces stress testing tools like LoadRunner, JMeter, and Gatling, plus professional DDoS simulation testing services to ensure your defenses are truly effective.
High ConcurrencyCloud High Concurrency Architecture: AWS, GCP, Azure Solutions Comparison & Best Practices | 2025
How does cloud handle high concurrency? This article compares high concurrency solutions from AWS, GCP, and Azure, including Auto Scaling, ElastiCache, Lambda serverless architecture, plus cost analysis and hybrid cloud strategy recommendations.
High ConcurrencyHigh Concurrency Architecture Design: Evolution from Monolith to Microservices | 2025 Practical Guide
How to design high concurrency architecture? This article covers monolithic architecture bottlenecks, choosing between vertical and horizontal scaling, layered architecture design principles, and microservices decomposition strategies. Includes service governance, configuration centers, and AWS, GCP, Azure cloud architecture recommendations.