Zum Inhalt springen

wrk

wrk ist ein modernes HTTP-Benchmarking-Tool, das ein ereignisgesteuertes, multithreaded Design verwendet, um erhebliche Last auf Webservern zu erzeugen. Es unterstützt Lua-Scripting für benutzerdefinierte Request-Generierung, Response-Verarbeitung und komplexe Testszenarien.

Installation

# Ubuntu/Debian - build from source
sudo apt-get install build-essential libssl-dev git
git clone https://github.com/wg/wrk.git
cd wrk
make -j$(nproc)
sudo cp wrk /usr/local/bin/

# macOS
brew install wrk

# Verify installation
wrk --version

Einfache Benchmarks

# Simple benchmark: 2 threads, 10 connections, 30 seconds
wrk -t2 -c10 -d30s http://localhost:8080/

# Higher load: 12 threads, 400 connections, 30 seconds
wrk -t12 -c400 -d30s http://localhost:8080/api/items

# Short burst test: 4 threads, 100 connections, 10 seconds
wrk -t4 -c100 -d10s http://localhost:8080/health

# With custom headers
wrk -t4 -c100 -d30s -H "Authorization: Bearer token123" \
    -H "Content-Type: application/json" \
    http://localhost:8080/api/data

Threads und Verbindungen

# Thread and connection guidelines:
# - Threads (-t): usually match CPU cores (2-16)
# - Connections (-c): total concurrent connections across all threads
# - Each thread handles connections/threads connections

# Light load
wrk -t1 -c10 -d10s http://localhost:8080/

# Medium load
wrk -t4 -c100 -d30s http://localhost:8080/

# Heavy load
wrk -t8 -c500 -d60s http://localhost:8080/

# Maximum stress test
wrk -t12 -c1000 -d60s http://localhost:8080/

# Timeout per request (default: 2s)
wrk -t4 -c100 -d30s --timeout 5s http://localhost:8080/

Daueroptionen

# Seconds
wrk -t4 -c100 -d30s http://localhost:8080/

# Minutes
wrk -t4 -c100 -d5m http://localhost:8080/

# With latency distribution breakdown
wrk -t4 -c100 -d30s --latency http://localhost:8080/
# Shows 50%, 75%, 90%, 99% percentile latencies

Ausgabe lesen

# Example wrk output:
# Running 30s test @ http://localhost:8080/
#   4 threads and 100 connections
#   Thread Stats   Avg      Stdev     Max   +/- Stdev
#     Latency     5.23ms    2.41ms  48.32ms   78.15%
#     Req/Sec     4.87k   312.45     6.12k    71.25%
#   Latency Distribution
#     50%    4.82ms
#     75%    6.15ms
#     90%    8.23ms
#     99%   14.51ms
#   582341 requests in 30.01s, 1.23GB read
# Requests/sec:  19405.12
# Transfer/sec:     42.01MB

# Key metrics:
# Latency Avg - average response time
# Req/Sec - requests per second per thread
# Requests/sec - total throughput
# Non-2xx or 3xx responses - errors (shown if any)

Lua-Scripting

Custom Request Generation

-- post.lua - Send POST requests with JSON body
wrk.method = "POST"
wrk.body   = '{"username": "test", "action": "login"}'
wrk.headers["Content-Type"] = "application/json"
# Run with Lua script
wrk -t4 -c100 -d30s -s post.lua http://localhost:8080/api/login

Dynamic Request Bodies

-- dynamic.lua - Generate unique requests per thread
local counter = 0
local threads = {}

function setup(thread)
    table.insert(threads, thread)
    thread:set("id", counter)
    counter = counter + 1
end

function init(args)
    requests = 0
    local id = wrk.thread:get("id")
end

function request()
    requests = requests + 1
    local body = string.format('{"id": %d, "seq": %d}', wrk.thread:get("id"), requests)
    return wrk.format("POST", "/api/items", {
        ["Content-Type"] = "application/json"
    }, body)
end

Response Processing

-- response.lua - Process and log responses
local responses = {}

function response(status, headers, body)
    if responses[status] == nil then
        responses[status] = 0
    end
    responses[status] = responses[status] + 1
end

function done(summary, latency, requests)
    io.write("Status code distribution:\n")
    for status, count in pairs(responses) do
        io.write(string.format("  [%d] %d responses\n", status, count))
    end

    io.write(string.format("\nLatency percentiles:\n"))
    for _, p in pairs({50, 75, 90, 99, 99.9}) do
        io.write(string.format("  %5.1f%%: %s\n", p,
            tostring(latency:percentile(p) / 1000) .. "ms"))
    end

    io.write(string.format("\nTotal requests: %d\n", summary.requests))
    io.write(string.format("Total errors:   %d\n", summary.errors.status))
end

Request Pipeline

-- pipeline.lua - HTTP pipelining (multiple requests per connection)
init = function(args)
    local r = {}
    -- Pipeline 10 requests at a time
    for i = 1, 10 do
        r[i] = wrk.format("GET", "/api/item/" .. i)
    end
    req = table.concat(r)
end

request = function()
    return req
end

Authentication Flow

-- auth.lua - Authenticate first, then benchmark
local token = nil

function setup(thread)
    -- This runs once per thread
end

function init(args)
    -- Authenticate to get a token
    local headers = {["Content-Type"] = "application/json"}
    local body = '{"username": "loadtest", "password": "test123"}'
    -- Token would be set from response in a real scenario
    token = "Bearer test-token-123"
end

function request()
    return wrk.format("GET", "/api/protected", {
        ["Authorization"] = token,
        ["Accept"] = "application/json"
    })
end

wrk2 Variant

# wrk2 adds constant-throughput rate limiting
# Install wrk2
git clone https://github.com/giltene/wrk2.git
cd wrk2
make -j$(nproc)
sudo cp wrk /usr/local/bin/wrk2

# Run with target rate (requests/second)
wrk2 -t4 -c100 -d60s -R2000 http://localhost:8080/
# -R2000 = target 2000 requests per second

# wrk2 provides more accurate latency measurements
# by avoiding coordinated omission problem

# Compare wrk vs wrk2:
# wrk  = open-loop (as fast as possible)
# wrk2 = closed-loop (constant rate, better latency stats)

Häufige Testmuster

# Quick smoke test
wrk -t1 -c1 -d5s http://localhost:8080/health

# Baseline performance
wrk -t2 -c10 -d30s --latency http://localhost:8080/

# Connection scaling test (run multiple times, increase -c)
for c in 10 50 100 200 500 1000; do
    echo "=== $c connections ==="
    wrk -t4 -c$c -d15s --latency http://localhost:8080/
    sleep 2
done

# Compare endpoints
for endpoint in /api/v1/users /api/v1/items /api/v1/search; do
    echo "=== $endpoint ==="
    wrk -t4 -c100 -d30s --latency http://localhost:8080$endpoint
done

Tipps

# Check system limits before high-connection tests
ulimit -n  # Open files limit
# Increase if needed
ulimit -n 65535

# Monitor server during benchmarks
# (in another terminal)
watch -n1 'ss -s'  # Socket statistics
vmstat 1            # System stats