Stop guessing. Start measuring. 🎯 Engineering decisions should be backed by hard data, not hunches. Make data-driven choices for your HTTP stack with precision, high-concurrency benchmarking.
In the high-stakes world of performance-critical services, your choice of HTTP client, server infrastructure, and request handling isn't just a detail—it's the backbone of your application's scalability. This framework eliminates the guesswork by providing comprehensive, real-world benchmarks across your entire HTTP ecosystem.
Most benchmarking tools focus on either just the client or just the server. We take a holistic, multi-dimensional approach to help you optimize the three critical pillars of your HTTP infrastructure:
Find the perfect library for your specific workload. Compare the battle-tested requests, the modern httpx, or the high-performance aiohttp and pycurl. Get the numbers, not the hype.
Available Arsenal:
- 🐍
requests— The battle-tested industry standard - ⚡
httpx— Modern, feature-rich HTTP/1.1 & HTTP/2 with sync/async flexibility - 🌊
aiohttp— The high-performance async engine for non-blocking I/O - 🔗
pycurl— Blazing fast C-level bindings via libcurl - 🚄
requestx— Performance-tuned dual-mode execution - 🔌
urllib3— Rock-solid connection pooling at the core
Don't test in a vacuum. Benchmark against production-grade environments. Compare how different reverse proxies and load balancers handle the heat.
Battlefield Scenarios:
- 🎈 Simple HTTPBin — Lightning-fast validation with a lightweight instance
- 🎪 Traefik Load Balancer — Modern, cloud-native proxying across triple backend instances
- 🚀 Nginx Load Balancer — Battle-hardened, high-throughput reverse proxy simulation
Performance isn't uniform. A GET request behaves differently than a heavy POST. Benchmark the exact operations your users actually perform with full support for the entire HTTP method specification.
✅ Infinite Combinations — Mix and match any client, server, and method for 360° coverage
✅ Granular Telemetry — Track throughput (RPS), p95/p99 latency, and real-time CPU/Memory usage
✅ Long-term Analysis — Built-in SQLite persistence for historical trend tracking and regression testing
✅ Production Parity — Fully supports HTTPS, load balancers, and multi-instance topologies
✅ Stealth Monitoring — Background resource sampling ensures zero interference with benchmark accuracy
✅ Developer First — Modular adapter pattern makes adding custom clients a breeze
Run a head-to-head comparison between top libraries using high-concurrency POST requests against an Nginx-backed cluster:
python -m http_benchmark.cli \
--url https://localhost/post \
--method POST \
--body '{"test": "data"}' \
--compare requests httpx aiohttp \
--concurrency 5 \
--duration 1The Result? Cold, hard facts delivered straight to your console. End the architecture debates and start building on a foundation of measured performance.
The framework is built with extensibility in mind, featuring a clean adapter layer for HTTP clients, a non-blocking resource monitoring system, and a robust persistence layer.
┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐
│ CLI/API │ │ HTTP Client │ │ Test Server │
│ Benchmark │───▶│ (requests/ │───▶│ (httpbin/ │
│ Config │ │ httpx/etc) │ │ traefik/ │
└─────────────────┘ └─────────────────┘ │ nginx) │
│ └─────────────────┘
│ │
▼ ▼
┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐
│ Console │◀────│ Results │◀────│ Performance │
│ Output │ │ Processing │ │ Metrics │
│ │ │ │ │ Collection │
└─────────────────┘ └─────────────────┘ └─────────────────┘
│ │
│ ▼
│ ┌─────────────────┐
└───────────▶│ SQLite DB │
│ Storage │
└─────────────────┘
Data Flow:
- Configure: Define client, server, method, and concurrency parameters.
- Execute: Launch high-frequency requests while monitoring system resources in the background.
- Analyze: Aggregate performance metrics including throughput and latency percentiles.
- Persist: Store detailed results in SQLite for historical analysis.
- Report: Visualize comparative data directly in your terminal.
- Python 3.12+
- Docker & Docker Compose (for running isolated test servers)
-
Clone the repository:
git clone https://github.com/your-repo/http-client-benchmarker.git cd http-client-benchmarker -
Install dependencies using
uv(recommended for speed):uv venv source .venv/bin/activate # On Windows: .venv\Scripts\activate uv pip install -e ".[dev]"
Or using standard
pip:python -m venv .venv source .venv/bin/activate # On Windows: .venv\Scripts\activate pip install -e ".[dev]"
Choose a server configuration that mirrors your production environment:
# Option 1: Simple HTTPBin (single instance, HTTP only)
docker-compose -f httpbin_server/docker-compose.httpbin.yml up -d
# Option 2: Traefik Load Balancer (3 instances, HTTP/HTTPS, cloud-native)
docker-compose -f httpbin_server/docker-compose.traefik.yml up -d
# Option 3: Nginx Load Balancer (3 instances, HTTP/HTTPS, high performance)
docker-compose -f httpbin_server/docker-compose.nginx.yml up -d| Feature | 🎈 Simple HTTPBin | 🎪 Traefik | 🚀 Nginx |
|---|---|---|---|
| Backend Instances | 1 | 3 | 3 |
| HTTP Support | ✅ | ✅ | ✅ |
| HTTPS Support | ❌ | ✅ | ✅ |
| Load Balancing | ❌ | ✅ | ✅ |
| Resource Overhead | Low | High | Medium |
| Best For | Quick tests | Real-world simulation | Raw performance |
Single Client Benchmark:
python -m http_benchmark.cli \
--url http://localhost/get \
--client httpx \
--concurrency 5 \
--duration 2Head-to-Head Comparison:
python -m http_benchmark.cli \
--url http://localhost/get \
--compare requests httpx aiohttp \
--concurrency 5 \
--duration 2Different HTTP Methods:
# POST with payload
python -m http_benchmark.cli --url http://localhost/post --method POST --body '{"user": "test"}' --client httpx --concurrency 1 --duration 1
# PUT, PATCH, DELETE
python -m http_benchmark.cli --url http://localhost/put --method PUT --client aiohttp --concurrency 1 --duration 1The framework can be used programmatically as a Python library for fine-grained control and integration into your own testing infrastructure.
Basic Usage:
from http_benchmark.benchmark import BenchmarkRunner
from http_benchmark.models.benchmark_configuration import BenchmarkConfiguration
from http_benchmark.storage import ResultStorage
# Configure your benchmark
config = BenchmarkConfiguration(
target_url="http://localhost/get",
http_method="GET",
concurrency=10,
duration_seconds=30,
client_library="requests",
is_async=False,
verify_ssl=False,
timeout=30,
)
# Run the benchmark
runner = BenchmarkRunner(config)
result = runner.run()
# Access results
print(f"RPS: {result.requests_per_second:.2f}")
print(f"Avg Latency: {result.avg_response_time * 1000:.2f}ms")
print(f"P95 Latency: {result.p95_response_time * 1000:.2f}ms")
# Persist results
storage = ResultStorage()
storage.save_result(result)Compare Multiple Clients:
# Compare multiple HTTP clients
clients = ["requests", "httpx", "aiohttp", "urllib3", "pycurl", "requestx"]
results = {}
for client in clients:
is_async = client in ("aiohttp", "requestx-async")
actual_client = client.replace("-async", "")
config = BenchmarkConfiguration(
target_url="http://localhost/get",
http_method="GET",
concurrency=5,
duration_seconds=10,
client_library=actual_client,
is_async=is_async,
)
runner = BenchmarkRunner(config)
result = runner.run()
results[client] = result
print(f"{client}: {result.requests_per_second:.2f} RPS")
# Find the fastest
best = max(results.items(), key=lambda x: x[1].requests_per_second)
print(f"\nFastest: {best[0]} ({best[1].requests_per_second:.2f} RPS)")Async Usage:
# For async clients (aiohttp, httpx with is_async=True)
config = BenchmarkConfiguration(
target_url="http://localhost/get",
http_method="GET",
concurrency=100,
duration_seconds=30,
client_library="aiohttp",
is_async=True,
)
runner = BenchmarkRunner(config)
result = runner.run()
print(f"Async RPS: {result.requests_per_second:.2f}")Scenario: Your team is considering migrating from requests to httpx to leverage HTTP/2.
Solution: Run a 60-second high-concurrency comparison to quantify RPS, latency, and resource usage.
Scenario: Your API's POST endpoints feel sluggish compared to GET requests.
Solution: Separately benchmark GET and POST methods with realistic payloads to identify the bottleneck.
Scenario: Choosing between Nginx and Traefik for your production load balancer.
Solution: Swap Docker Compose environments and run identical benchmark suites to see which proxy handles the load better.
| Library | Sync | Async | Key Characteristics |
|---|---|---|---|
| aiohttp | ❌ | ✅ | Non-blocking I/O, optimal for async services, built-in connection pooling |
| httpx | ✅ | ✅ | HTTP/2 support, requests-compatible API, modern design |
| pycurl | ✅ | ❌ | libcurl bindings, minimal overhead, C-level performance |
| requests | ✅ | ❌ | Industry standard, extensive ecosystem, blocking I/O |
| requestx | ✅ | ✅ | Performance-optimized fork, dual-mode execution |
| urllib3 | ✅ | ❌ | Foundation library, thread-safe pooling, low-level control |
All benchmark results are persisted to SQLite for long-term trend analysis and data-driven decision making.
| Field | Type | Description |
|---|---|---|
id |
TEXT | Primary key (UUID) |
name |
TEXT | Benchmark run identifier |
client_library |
TEXT | Library name (e.g., "httpx") |
client_type |
TEXT | Execution model ("sync" or "async") |
http_method |
TEXT | HTTP method (GET, POST, etc.) |
url |
TEXT | Target URL |
start_time |
TEXT | Start timestamp (ISO 8601) |
end_time |
TEXT | End timestamp (ISO 8601) |
duration |
REAL | Total execution time (seconds) |
requests_count |
INTEGER | Total requests completed |
requests_per_second |
REAL | Average throughput (RPS) |
avg_response_time |
REAL | Mean latency (seconds) |
p95_response_time |
REAL | 95th percentile latency (seconds) |
p99_response_time |
REAL | 99th percentile latency (seconds) |
cpu_usage_avg |
REAL | Average CPU usage (%) |
memory_usage_avg |
REAL | Average RSS memory (MB) |
error_count |
INTEGER | Total failed requests |
error_rate |
REAL | Failure percentage (0-100) |
concurrency_level |
INTEGER | Configured concurrency |
config_snapshot |
TEXT | JSON snapshot of full configuration |
created_at |
TEXT | Record creation timestamp (ISO 8601) |
Compare Client Performance:
SELECT
client_library,
ROUND(AVG(requests_per_second), 2) as avg_rps,
ROUND(AVG(avg_response_time) * 1000, 2) as avg_latency_ms,
ROUND(AVG(cpu_usage_avg), 2) as avg_cpu_pct
FROM benchmark_results
WHERE http_method = 'GET'
GROUP BY client_library
ORDER BY avg_rps DESC;Track Performance Over Time:
SELECT
DATE(created_at) as benchmark_date,
client_library,
AVG(requests_per_second) as daily_avg_rps
FROM benchmark_results
WHERE client_library = 'httpx'
GROUP BY DATE(created_at), client_library
ORDER BY benchmark_date DESC;# Run all tests
python -m unittest discover tests
# Run specific suite
python -m unittest discover tests/unit
python -m unittest discover tests/integration
python -m unittest discover tests/performance# Format and lint
black http_benchmark/ tests/ --line-length 120
flake8 http_benchmark/ tests/ --max-line-length=120
mypy http_benchmark/- Create a new adapter in
http_benchmark/clients/inheriting fromBaseAdapter. - Register the adapter in
http_benchmark/benchmark.pywithinBenchmarkRunner. - Add corresponding unit tests in
tests/unit/.
The framework uses a clean adapter pattern to decouple the benchmarking engine from specific HTTP client implementations. Each adapter implements a unified interface, making it trivial to add new clients without modifying core logic.
A background thread continuously samples system metrics using psutil without interfering with benchmark execution. Metrics are collected at high frequency and aggregated post-benchmark.
- Synchronous Clients: Managed via
ThreadPoolExecutorwith optimized pool sizing. - Asynchronous Clients: Powered by
asynciowith task-based concurrency for maximum efficiency.
We welcome contributions! Please feel free to submit a Pull Request.
This project is licensed under the MIT License. See LICENSE for details.
Ready to optimize your HTTP stack? Start benchmarking now! 🚀