wrk
wrk é uma ferramenta moderna de benchmarking HTTP que usa um design orientado a eventos e multi-thread para gerar carga significativa em servidores web. Suporta scripts Lua para geração personalizada de requisições, processamento de respostas e cenários de teste complexos.
Instalação
# 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
Basic 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 and Connections
# 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/
Duration Opções
# 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
Reading Saída
# 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
Autenticação 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)
Common Test Patterns
# 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
Dicas
# 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