Load Testing Performance
Load Testing und Performance Testing
Load Testing zeigt, wie Ihre Anwendung unter Last reagiert. Lernen Sie Lasttests mit k6, Artillery und JMeter zu planen und durchzuführen.
Testarten verstehen
┌─────────────────────────────────────────────────────────────┐ │ PERFORMANCE TEST ARTEN │ ├─────────────────────────────────────────────────────────────┤ │ │ │ LOAD TEST │ │ ┌─────────────────────────────────────────────────────┐ │ │ │ Last ▲ │ │ │ │ │ ┌────────────────┐ │ │ │ │ │ │ Normal Load │ │ │ │ │ └────┴────────────────┴────────► Zeit │ │ │ │ → Wie verhält sich das System unter erwarteter Last│ │ │ └─────────────────────────────────────────────────────┘ │ │ │ │ STRESS TEST │ │ ┌─────────────────────────────────────────────────────┐ │ │ │ Last ▲ ╱╲ │ │ │ │ │ ╱ ╲ │ │ │ │ │ ╱ ╲ │ │ │ │ └──────╱────────────╲───────► Zeit │ │ │ │ → Ab welcher Last bricht das System zusammen? │ │ │ └─────────────────────────────────────────────────────┘ │ │ │ │ SPIKE TEST │ │ ┌─────────────────────────────────────────────────────┐ │ │ │ Last ▲ │ │ │ │ │ │ │ │ │ │ │ │ │ ────┴────┴──── │ │ │ │ └────────────────────────────► Zeit │ │ │ │ → Wie reagiert das System auf plötzliche Spitzen? │ │ │ └─────────────────────────────────────────────────────┘ │ │ │ │ SOAK TEST (Endurance) │ │ ┌─────────────────────────────────────────────────────┐ │ │ │ Last ▲ │ │ │ │ │ ───────────────────────────────────── │ │ │ │ │ │ │ │ │ └────────────────────────────► Zeit (Stunden) │ │ │ │ → Gibt es Memory Leaks oder Degradation über Zeit? │ │ │ └─────────────────────────────────────────────────────┘ │ │ │ └─────────────────────────────────────────────────────────────┘
k6 - Moderne Load Testing Tool
// Installation
// brew install k6
// apt install k6
// docker pull grafana/k6
// basic-test.js
import http from 'k6/http';
import { check, sleep } from 'k6';
// Test-Konfiguration
export const options = {
// Stages für Ramp-Up/Ramp-Down
stages: [
{ duration: '30s', target: 20 }, // Ramp up auf 20 User
{ duration: '1m', target: 20 }, // 1 Minute halten
{ duration: '30s', target: 50 }, // Auf 50 User erhöhen
{ duration: '2m', target: 50 }, // 2 Minuten halten
{ duration: '30s', target: 0 }, // Ramp down
],
// Schwellwerte
thresholds: {
http_req_duration: ['p(95)<500'], // 95% unter 500ms
http_req_failed: ['rate<0.01'], // Weniger als 1% Fehler
checks: ['rate>0.99'], // 99% der Checks bestehen
},
};
export default function () {
// Test-Szenario
const response = http.get('https://api.example.com/users');
// Assertions
check(response, {
'status is 200': (r) => r.status === 200,
'response time < 200ms': (r) => r.timings.duration < 200,
'has users': (r) => JSON.parse(r.body).users.length > 0,
});
sleep(1); // 1 Sekunde Pause zwischen Requests
}
// Komplexeres Szenario mit mehreren Endpoints
import http from 'k6/http';
import { check, group, sleep } from 'k6';
const BASE_URL = 'https://api.example.com';
export default function () {
// User Flow simulieren
group('User Login Flow', function () {
// 1. Login
const loginRes = http.post(`${BASE_URL}/auth/login`, JSON.stringify({
email: 'test@example.com',
password: 'password123',
}), {
headers: { 'Content-Type': 'application/json' },
});
check(loginRes, {
'login successful': (r) => r.status === 200,
'has token': (r) => JSON.parse(r.body).token !== undefined,
});
const token = JSON.parse(loginRes.body).token;
sleep(1);
// 2. Dashboard laden
const dashboardRes = http.get(`${BASE_URL}/dashboard`, {
headers: { Authorization: `Bearer ${token}` },
});
check(dashboardRes, {
'dashboard loaded': (r) => r.status === 200,
});
sleep(2);
// 3. Daten abrufen
const dataRes = http.get(`${BASE_URL}/api/data`, {
headers: { Authorization: `Bearer ${token}` },
});
check(dataRes, {
'data retrieved': (r) => r.status === 200,
'response time OK': (r) => r.timings.duration < 500,
});
});
sleep(3);
}
# k6 ausführen k6 run basic-test.js # Mit mehr Output k6 run --out json=results.json basic-test.js # Mit Grafana Cloud Integration k6 run --out cloud basic-test.js # Nur bestimmte Anzahl VUs k6 run --vus 10 --duration 30s basic-test.js
Artillery
# Installation
npm install -g artillery
# artillery.yml
config:
target: 'https://api.example.com'
phases:
- duration: 60
arrivalRate: 5
name: "Warm up"
- duration: 120
arrivalRate: 10
rampTo: 50
name: "Ramp up load"
- duration: 300
arrivalRate: 50
name: "Sustained load"
defaults:
headers:
Content-Type: 'application/json'
ensure:
p99: 500
maxErrorRate: 1
scenarios:
- name: "User Journey"
flow:
- post:
url: "/auth/login"
json:
email: "test@example.com"
password: "password123"
capture:
- json: "$.token"
as: "authToken"
- think: 2
- get:
url: "/api/users"
headers:
Authorization: "Bearer {{ authToken }}"
expect:
- statusCode: 200
- contentType: json
- think: 3
- get:
url: "/api/products"
expect:
- statusCode: 200
# Artillery ausführen artillery run artillery.yml # Mit Report artillery run --output report.json artillery.yml artillery report report.json --output report.html # Quick Test artillery quick --count 100 --num 10 https://api.example.com/health
JMeter (GUI-basiert)
# JMeter CLI Beispiel (ohne GUI) # Test Plan erstellen (testplan.jmx) # Enthält Thread Group, HTTP Requests, Listeners # Ausführen jmeter -n -t testplan.jmx -l results.jtl -e -o report/ # Parameter: # -n Non-GUI Mode # -t Test Plan File # -l Results Log # -e Generate Report # -o Report Output Folder # Mit Properties jmeter -n -t testplan.jmx \ -Jthreads=100 \ -Jrampup=60 \ -Jduration=300 \ -l results.jtl
Wichtige Metriken
RESPONSE TIME METRIKEN ┌────────────────┬────────────────────────────────────────────┐ │ Metrik │ Beschreibung │ ├────────────────┼────────────────────────────────────────────┤ │ Average │ Durchschnittliche Antwortzeit │ │ │ → Kann durch Ausreißer verfälscht sein │ ├────────────────┼────────────────────────────────────────────┤ │ Median (p50) │ 50% der Requests sind schneller │ │ │ → Besserer Indikator als Average │ ├────────────────┼────────────────────────────────────────────┤ │ p90 │ 90% der Requests sind schneller │ │ │ → Zeigt typische User Experience │ ├────────────────┼────────────────────────────────────────────┤ │ p95 │ 95% der Requests sind schneller │ │ │ → Standard SLA Metrik │ ├────────────────┼────────────────────────────────────────────┤ │ p99 │ 99% der Requests sind schneller │ │ │ → Worst-Case für normale User │ ├────────────────┼────────────────────────────────────────────┤ │ Max │ Längste Antwortzeit │ │ │ → Oft Ausreißer, weniger aussagekräftig │ └────────────────┴────────────────────────────────────────────┘ WICHTIG: p95 und p99 sind wichtiger als Average! THROUGHPUT METRIKEN • Requests per Second (RPS) • Transactions per Second (TPS) • Concurrent Users (VUs) ERROR METRIKEN • Error Rate (%) • Errors by Type (4xx, 5xx, Timeouts)
Test-Ergebnisse interpretieren
k6 OUTPUT BEISPIEL:
data_received..................: 15 MB 250 kB/s
data_sent......................: 2.1 MB 35 kB/s
http_req_blocked...............: avg=1.2ms med=0.9ms p(95)=2.5ms
http_req_connecting............: avg=0.8ms med=0.6ms p(95)=1.8ms
http_req_duration..............: avg=45ms med=38ms p(95)=120ms ← WICHTIG
http_req_failed................: 0.5% ← FEHLERRATE
http_req_receiving.............: avg=2ms med=1.5ms p(95)=5ms
http_req_sending...............: avg=0.3ms med=0.2ms p(95)=0.8ms
http_req_waiting...............: avg=42ms med=35ms p(95)=115ms
http_reqs......................: 50000 833/s ← THROUGHPUT
iteration_duration.............: avg=1.2s med=1.1s p(95)=1.5s
iterations.....................: 50000 833/s
vus............................: 50 min=1 max=50
vus_max........................: 50
INTERPRETATION:
✅ p(95) = 120ms - 95% unter 200ms Ziel
✅ Error Rate 0.5% - Unter 1% Schwelle
✅ 833 RPS - Ausreichend für erwartete Last
CI/CD Integration
# .github/workflows/load-test.yml
name: Load Test
on:
pull_request:
branches: [main]
schedule:
- cron: '0 2 * * *' # Täglich um 2 Uhr
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/api-test.js
- name: Upload Results
uses: actions/upload-artifact@v4
with:
name: load-test-results
path: results.json
- name: Check Thresholds
run: |
# Parse results und prüfe Schwellwerte
FAILED=$(jq '.metrics.checks.values.fails' results.json)
if [ "$FAILED" -gt 0 ]; then
echo "Load test failed: $FAILED checks failed"
exit 1
fi
Best Practices
PLANUNG 1. Baseline etablieren (aktuelle Performance messen) 2. Ziele definieren (SLAs, erwartete Last) 3. Realistische Szenarien (echte User Flows) 4. Test-Daten vorbereiten DURCHFÜHRUNG 1. Isolierte Umgebung (keine anderen Einflüsse) 2. Monitoring aktiviert (APM, Logs, Metriken) 3. Langsam hochfahren (Ramp-Up) 4. Mehrere Durchläufe für Konsistenz AUSWERTUNG 1. Percentiles statt Averages 2. Bottlenecks identifizieren (CPU, Memory, DB, Network) 3. Ergebnisse dokumentieren 4. Trends über Zeit verfolgen HÄUFIGE FEHLER ❌ Test von lokalem Rechner (Netzwerk-Limitierung) ❌ Zu wenig Test-Daten (Cache-Effekte) ❌ Keine Ramp-Up Phase (unrealistischer Spike) ❌ Nur Average betrachten
💡 Tipps:
1. Realistische User-Szenarien simulieren
2. p95/p99 für SLAs verwenden, nicht Average
3. Monitoring während Tests aktiv haben
4. Regelmäßig testen (nicht nur vor Release)
5. Ergebnisse dokumentieren und vergleichen
2. p95/p99 für SLAs verwenden, nicht Average
3. Monitoring während Tests aktiv haben
4. Regelmäßig testen (nicht nur vor Release)
5. Ergebnisse dokumentieren und vergleichen
Weitere Informationen
- 📊 Monitoring Metriken
- 💣 Chaos Engineering
- 📈 Enjyn Status Monitor - Performance Monitoring