Modules · Web · 0.63
Hyperman
An event-loop PSGI server: HTTP/1.1, HTTP/2, HTTP/3 and TLS in C.
A prefork supervisor with an XS event loop in every worker. A handler returns a response, or a future of one, and the connection is parked while the worker keeps serving. Any Plack application runs on it with plackup -s Hyperman, and this site does.
use Hyperman;
my $app = sub {
my $env = shift;
return [ 200, [ 'Content-Type' => 'text/plain' ], [ "hello\n" ] ]
unless $env->{PATH_INFO} eq '/slow';
# an async response: the worker serves other requests while this waits
Hyperman->timer(0.2)->then(sub {
# one counter, in the memory every worker maps
my $n = Hyperman->arena->map('hits', slots => 64)->incr('slow');
[ 200, [ 'Content-Type' => 'text/plain' ], [ "slow request $n\n" ] ];
});
};
Hyperman->run(app => $app, port => $ENV{PORT} || 8080, workers => 4, arena_size => 8_000_000);
What you get
-
A loop in every worker
A supervisor forks the workers and each runs its own XS event loop on kqueue, epoll, io_uring or poll, whichever the machine has.
-
Sync or a future
A handler returns a PSGI response or a future of one. While a future is pending the connection is parked and the worker serves others. Streaming works as in any PSGI server.
-
HTTP/2, HTTP/3 and TLS
With the nghttp2, ngtcp2 and OpenSSL builds: h2 by ALPN and h2c on the same port, HTTP/3 over QUIC advertised by
Alt-Svc, SNI, client certificates and reloading a certificate without a restart. -
Memory the workers share
A Shared::Arena made before the fork:
Hyperman->arenagives every worker the same maps, leases and counters, and a pool-widepublishandsubscribebus needs no Redis. -
Refused at accept
The denylist and the rate counters live in that arena, so a limit of 100 a minute is 100 for the pool, not 100 per worker, and a blocked peer is closed before a byte is read.
-
A C ABI
Loops, watchers, timers, futures and the bus are reachable from another module's C. Punk, DBIx::Loop and Fetch's Hyperman loop use it.
In use
# any PSGI application, under plackup
plackup -s Hyperman --port 8080 --workers 4 app.psgi
# HTTP/2 and TLS on the same port (with the nghttp2 and OpenSSL builds)
plackup -s Hyperman --port 8443 --http2 1 \
--tls_cert cert.pem --tls_key key.pem app.psgi
# choose the event backend instead of letting it choose
HYPERMAN_BACKEND=poll plackup -s Hyperman app.psgi
Fewer syscalls, said honestly
On the io_uring backend Hyperman can ask the kernel for each read up front, so the read stops being a system call of its own. Over 2,000 keep-alive requests on 50 connections, as its POD records it: 2.02 system calls a request with readiness, 1.02 in completion mode.
And it has not meant faster. On a virtualised Linux the same work costs about 4.2 microseconds of server CPU a request under io_uring and about 2.9 under epoll, so epoll stays ahead and io_uring stays off the automatic choice until that is understood on real hardware.
For C
hm_abi.h: another XS module drives the worker's loop and futures entirely from C, with fd watchers and timers whose callbacks run with no Perl frame per event. DBIx::Loop settles database futures this way. The table is resolved at run time, so a consumer can vendor a pinned copy and keep Hyperman optional.