k6 and Locust
The important skill is not memorizing three tools. It is recognizing how each tool expresses the same performance concepts: workload, user behavior, timing, metrics, pass/fail objectives and scaling of the generator.
k6: code-first scenarios
k6 scripts are JavaScript, while workload scheduling is configured in options. A compact closed-model example is:
import http from 'k6/http';
import { check, sleep } from 'k6';
export const options = {
scenarios: {
checkout_load: {
executor: 'ramping-vus',
stages: [
{ duration: '1m', target: 20 },
{ duration: '5m', target: 20 },
{ duration: '1m', target: 0 },
],
},
},
thresholds: {
http_req_failed: ['rate<0.01'],
http_req_duration: ['p(95)<500'],
},
};
export default function () {
const response = http.get('https://test.example/api/products');
check(response, { 'status is 200': (r) => r.status === 200 });
sleep(1);
}The threshold values are syntax examples. Real limits must come from the product's objectives.
k6 executors and workload meaning
The executor is part of the test model:
constant-vus,ramping-vus— load expressed through a VU population;constant-arrival-rate,ramping-arrival-rate— load expressed through iteration arrivals.
Arrival-rate executors implement an open model. Use them when real arrivals continue independently of how slowly current users are being served.
Example:
export const options = {
scenarios: {
api_arrivals: {
executor: 'constant-arrival-rate',
rate: 50,
timeUnit: '1s',
duration: '5m',
preAllocatedVUs: 100,
},
},
};That expresses an intent to start 50 iterations per second. Always confirm the generator actually maintains the intended rate and watch dropped/insufficient-capacity signals.
k6 thresholds
Thresholds are one of k6's strongest CI concepts. They turn metrics into a process exit status. Typical dimensions are percentile latency, errors and business checks. Keep the gate tied to a specific workload and stable environment; a threshold without those conditions is not reproducible.
Locust: user behavior in Python
Locust models behavior as Python user classes and tasks.
from locust import HttpUser, between, task
class ApiUser(HttpUser):
wait_time = between(1, 3)
@task(3)
def list_products(self):
with self.client.get('/api/products', catch_response=True) as response:
if response.status_code != 200:
response.failure(f'unexpected status {response.status_code}')
@task(1)
def health(self):
self.client.get('/api/health')Task weights model relative behavior; wait_time controls the delay between tasks. This is a workload description, not a guarantee of a specific RPS.
Headless Locust
locust -f locustfile.py --headless -H https://test.example -u 200 -r 20 -t 15mAccording to Locust configuration semantics:
-u 200— peak concurrent users;-r 20— spawn 20 users per second;-t 15m— run-time limit.
The spawn rate tells you how quickly the user population is reached, not the request throughput.
Distributed Locust
When one process or machine cannot generate the needed load, Locust supports master/worker execution. The master coordinates the run and aggregates statistics; workers run users. Recent Locust documentation recommends multiple worker processes when CPU becomes the generator constraint.
Example topology:
# master
locust -f locustfile.py --master
# worker
locust -f locustfile.py --worker --master-host 10.0.0.10Monitor worker CPU and achieved RPS. A load generator that cannot reach the requested load invalidates the conclusion about target capacity.
Tool-selection principle
Choose by protocol support, workload model, scripting/correlation needs, expected scale, CI/reporting integration, team skills and operational constraints. Use a small proof of concept and measure generator headroom. Tool popularity is not a test requirement.