91 lines
4.0 KiB
Markdown
91 lines
4.0 KiB
Markdown
# h3blast
|
||||
|
|
|
|||
|
|
A small, very fast HTTP/3 load generator built directly on **lsquic**. It exists
|
|||
|
|
to stress `Bun.serve({ http3: true })` without going through curl or a Node QUIC
|
|||
|
|
shim — same lsquic, same BoringSSL, same packet path as the server side.
|
|||
|
|
|
|||
|
|
```
|
|||
|
|
h3blast · HTTP/3 8 threads × 8 connections × 32 streams
|
|||
|
|
────────────────────────────────────────────────────────────────
|
|||
|
|
|
|||
|
|
static 406,114 req/s
|
|||
|
|
GET localhost:3001/hi
|
|||
|
|
4,061,140 requests in 10.00s 66.12 B/req · p99 1.10ms
|
|||
|
|
|
|||
|
|
|
|||
|
|
js 172,600 req/s
|
|||
|
|
GET localhost:3001/
|
|||
|
|
1,726,000 requests in 10.00s 46.06 B/req · p99 2.44ms
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
The bytes/req figure is wire bytes received (QUIC payload — headers + body +
|
|||
|
|
framing). Pass `-v` for per-target request totals, body-only bytes, total
|
|||
|
|
transfer, and the full latency-percentile / status-code charts.
|
|||
|
|
|
|||
|
|
## Design
|
|||
|
|
|
|||
|
|
One process, **N worker threads**. Each worker owns one non-blocking UDP
|
|||
|
|
socket, one lsquic client engine, and `connections / threads` QUIC connections.
|
|||
|
|
Each connection keeps `--streams` HTTP/3 request streams in flight at all
|
|||
|
|
times. UDP I/O is batched with `sendmmsg`/`recvmmsg`; the lsquic timer wheel
|
|||
|
|
is driven via `epoll` + `timerfd`. The main thread samples per-worker atomic
|
|||
|
|
counters at ~10 Hz to render the live TUI, then aggregates per-worker
|
|||
|
|
HdrHistograms for the final percentile report.
|
|||
|
|
|
|||
|
|
No TLS verification by default (it's a load tester).
|
|||
|
|
|
|||
|
|
## Build
|
|||
|
|
|
|||
|
|
h3blast links the **already-compiled** lsquic / BoringSSL / HdrHistogram / zlib
|
|||
|
|
object files from Bun's build tree, so you need a Bun release build first:
|
|||
|
|
|
|||
|
|
```sh
|
|||
|
|
cd ../.. # repo root
|
|||
|
|
bun run build:release # populates build/release/obj/vendor/**
|
|||
|
|
cd packages/h3blast
|
|||
|
|
make # → ./h3blast
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
`PROFILE=debug make` links against the debug objects instead.
|
|||
|
|
|
|||
|
|
## Usage
|
|||
|
|
|
|||
|
|
```sh
|
|||
|
|
./h3blast -t 4 -c 8 -m 64 -d 30 https://127.0.0.1:3000/
|
|||
|
|
./h3blast -X POST -H 'content-type: application/json' -b '{"a":1}' https://host/api
|
|||
|
|
./h3blast --json -d 5 https://host/ | jq '.[0].req_per_sec'
|
|||
|
|
|
|||
|
|
# compare two servers side-by-side — each positional is [label=]url
|
|||
|
|
./h3blast -c 4 -m 32 -d 10 bun=https://127.0.0.1:3443/ node=https://127.0.0.1:3444/
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
When multiple URLs are given they are benchmarked **sequentially** (same
|
|||
|
|
`-t`/`-c`/`-m`/`-d` each) so they don't compete for client CPU. The live TUI
|
|||
|
|
shows finished targets above the active one; the final report has one labelled
|
|||
|
|
block per target with average response-body size. The full latency-percentile
|
|||
|
|
and status-code charts are only printed with `-v/--verbose`; the default report
|
|||
|
|
shows just `body B/req` and `p50/p99`.
|
|||
|
|
|
|||
|
|
| flag | meaning |
|
|||
|
|
| -------------------------- | --------------------------------------------- |
|
|||
|
|
| `-t, --threads N` | worker threads |
|
|||
|
|
| `-c, --connections N` | total QUIC connections (split across threads) |
|
|||
|
|
| `-m, --streams N` | concurrent request streams per connection |
|
|||
|
|
| `-d, --duration SEC` | run length (default 10) |
|
|||
|
|
| `-n, --requests N` | stop after N total responses |
|
|||
|
|
| `-X, --method M` | HTTP method |
|
|||
|
|
| `-H 'k: v'` | extra request header (repeatable) |
|
|||
|
|
| `-b STR` / `--body-file P` | request body |
|
|||
|
|
| `--warmup SEC` | ignore stats from the first SEC seconds |
|
|||
|
|
| `--json` | machine-readable summary (array of targets) |
|
|||
|
|
| `-v, --verbose` | full latency/status charts in final report |
|
|||
|
|
| `--no-color`, `-q` | disable color / live UI |
|
|||
|
|
| `H3BLAST_DEBUG=debug` | turn on lsquic's internal logger |
|
|||
|
|
|
|||
|
|
## Against a local Bun H3 server
|
|||
|
|
|
|||
|
|
```sh
|
|||
|
|
bun test-server.js 3443 &
|
|||
|
|
./h3blast -t 2 -c 4 -m 32 -d 10 https://127.0.0.1:3443/
|
|||
|
|
```
|