- Web Server reads static files inline; proxy middleware chain flattened - About window with license summary and project links; AGPL-3.0-or-later - Rewritten documentation: feature list, screenshots, performance guide - Fix false spikes in per-Vhost request-rate charts Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01MMsgSXoK4Bph2vU6fSBkAH
73 lines
4.9 KiB
Markdown
73 lines
4.9 KiB
Markdown
# Performance
|
|
|
|
RustProxy runs on Tokio and Hyper with an asynchronous, streaming request path. This guide describes how performance is measured, where RustProxy stands compared with a similarly configured Nginx, and which settings matter in production.
|
|
|
|
## What is measured
|
|
|
|
Results are only comparable when both servers do the same work. A stock Nginx installation does very little per request, while RustProxy always records request statistics, traffic analytics and access logs and adds forwarding headers. The comparison therefore uses an Nginx configuration with a similar feature set:
|
|
|
|
| Feature | Nginx | RustProxy |
|
|
|---|---|---|
|
|
| Access log | `log_format` with timing fields, 64 KiB buffer | Built-in access log and live statistics (default) |
|
|
| Forwarding headers | `Host`, `X-Forwarded-For`, `X-Forwarded-Proto`, `X-Real-IP` | `Forwarded`, `X-Forwarded-*`, `X-Real-IP`, `X-Request-Id` (default) |
|
|
| Response headers | `X-Request-ID`, `X-Content-Type-Options` | Request ID and default security headers |
|
|
| Upstream keep-alive | `keepalive 64`, HTTP/1.1 | Connection pool (default) |
|
|
| Cache | `proxy_cache` with `proxy_cache_lock` | HOT memory cache with request collapsing |
|
|
| Compression | `gzip on` | gzip/Brotli/Zstandard negotiation (default) |
|
|
| Static files | `root`, `etag on` | Web Service with validators (default) |
|
|
|
|
## Method
|
|
|
|
- Machine: 2 vCPU virtual machine, Linux 6.18, loopback networking.
|
|
- Each server is pinned to one CPU core (`worker_processes 1` for Nginx, `taskset` for RustProxy). The load generator and the origin share the second core.
|
|
- Load: `wrk -t1 -c50 -d8s`, HTTP/1.1 keep-alive, two runs per scenario, average reported.
|
|
- Origin: Nginx serving static files with `Cache-Control: public, max-age=600`.
|
|
- Nginx 1.24 and RustProxy release builds (`lto = "thin"`, `codegen-units = 1`).
|
|
|
|
## Results
|
|
|
|
Requests per second on one CPU core (higher is better):
|
|
|
|
| Scenario | Nginx | RustProxy 0.35.0 | RustProxy 0.35.1 | 0.35.1 vs 0.35.0 |
|
|
|---|---:|---:|---:|---:|
|
|
| Reverse proxy, 1 KiB response | 54,700 | 12,700 | 14,200 | +12% |
|
|
| Reverse proxy, 100 KiB response | 9,600 | 11,400 | 13,600 | +19% |
|
|
| Cache hit, 1 KiB response | 51,000 | 19,500 | 23,600 | +21% |
|
|
| Web Server, 1 KiB file | 78,500 | 13,400 | 23,900 | +78% |
|
|
| Web Server, 100 KiB file | 22,500 | 8,000 | 14,900 | +86% |
|
|
|
|
Reading the results:
|
|
|
|
- **Large proxied responses:** RustProxy streams the body directly between connections and is faster than Nginx in its default buffering mode, which spools larger upstream responses to temporary files.
|
|
- **Small requests:** Nginx remains ahead, because its fixed per-request cost is lower. In RustProxy this cost is dominated by per-request accounting (live statistics, traffic analytics, request IDs and logs) and by the asynchronous runtime. For most deployments it is far below the capacity of the backends behind the proxy.
|
|
- **Static files:** 0.35.1 reads files directly on the request task instead of a background thread pool, which nearly doubles static throughput.
|
|
- **CPU per request:** at the 1 KiB proxy scenario RustProxy uses about 70 µs of CPU time per request on the test machine, Nginx about 17 µs. A single core therefore sustains well over 10,000 proxied requests per second.
|
|
|
|
Throughput scales with additional CPU cores; RustProxy uses all available cores automatically.
|
|
|
|
## Production tuning
|
|
|
|
| Setting | Recommendation |
|
|
|---|---|
|
|
| Cache | Enable the HOT cache for cacheable content; hits avoid the backend completely. |
|
|
| Compression | Keep it enabled for text content. Use precompressed `.br`/`.gz` files for static sites. |
|
|
| Upstream protocol | Prefer HTTP/1.1 keep-alive or HTTP/2 to backends; avoid backends that close every connection. |
|
|
| Health checks | Use intervals of several seconds; frequent probes consume capacity on large installations. |
|
|
| Logging | Keep stored log files on fast local storage and use retention limits on busy servers. |
|
|
| Limits | `--max-connections` (default 512) and `--max-inflight` (default 256) cap concurrency. Raise them on busy servers with enough memory. |
|
|
| `sendfile` | Optional for large cleartext static files on Linux. On the test machine it gave no measurable gain, so keep `off` unless your own measurements show otherwise. |
|
|
| TLS | TLS 1.3 with session resumption keeps handshake cost low; HTTP/2 or HTTP/3 reduce connection setup for browsers. |
|
|
|
|
## Reproducing the test
|
|
|
|
1. Start an origin (any static HTTP server) with a 1 KiB and a 100 KiB file.
|
|
2. Configure RustProxy with one Vhost pointing to the origin and one Web Service serving the same directory.
|
|
3. Configure Nginx with the equivalent features from the table above.
|
|
4. Pin each server to the same number of cores and run `wrk` from a different core:
|
|
|
|
```bash
|
|
taskset -c 1 wrk -t1 -c50 -d8s -H "Host: proxy.example" http://127.0.0.1:8080/1k.txt
|
|
```
|
|
|
|
Always compare matched configurations on the same machine, and repeat each run. Results on physical hardware and real networks differ from loopback measurements.
|