Roadrunner - Pure-Erlang HTTP and WebSocket server

Hey @etnt!

I ran roadrunner against the built-in httpd, using the same load test we use for cowboy and elli. Two httpd setups: plain httpd (a small hand-written do/1, no extra deps) and httpd + httpd_router.

Median of 3 runs, 50 clients, HTTP/1.1, loopback, OTP 29 (req/s, higher is better):

scenario roadrunner httpd httpd + httpd_router
hello 292k 183k 178k
json 266k 164k 156k
echo 253k 149k 138k
routing, 100 paths 244k 150k 64k

A few friendly notes:

  • The built-in server is fast, and httpd_router adds almost no cost when there is just one route. Nice work.
  • The slow part is matching many routes: httpd_router reads and scans its full route list on every request (ets:tab2list/1), so it slows down with 100 routes. Keeping the routes ready instead of rebuilding them each time would help a lot here.
  • One tip if you test httpd yourself: by default it groups small TCP writes together (Nagle’s algorithm), which adds about 40ms to each keep-alive request on loopback. Turn it off with {socket_type, {ip_comm, [{nodelay, true}]}} and helloscenario goes from ~1k to ~180k req/s.

On features, the built-in httpd is HTTP/1.1 only, which is the trade-off for staying dependency-free:

feature roadrunner httpd httpd + httpd_router
HTTP/2 yes no no
HTTP/3 yes no no
WebSocket yes no no
routing + path params yes manual yes
extra deps telemetry none httpd_router

So if you want to stay dependency-free and only need HTTP/1.1, httpd is a solid pick, and httpd_router adds clean routing on top with almost no cost.

The numbers move around a bit (~15% each run), my machine was a little busy, and loopback (same machine) hides the real network cost, so take them as rough. I’m gonna add a full comparison in a PR shortly.

Cheers!

4 Likes