NAME
Feersum::Runner - feersum script core
SYNOPSIS
use Feersum::Runner;
my $runner = Feersum::Runner->new(
listen => 'localhost:5000',
pre_fork => 0,
quiet => 1,
app_file => 'app.feersum',
);
$runner->run($feersum_app);
DESCRIPTION
Process manager for Feersum. Handles listen sockets, pre-forking, hot restart, TLS, daemonization, and graceful shutdown.
METHODS
Feersum::Runner->new(%params)-
Returns a Feersum::Runner singleton. If called again while not running, the previous instance is replaced with a new one using the provided params.
- listen
-
Listen address as an arrayref containing one or more address strings, e.g.,
listen => ['localhost:5000'], or a plain string for a single address. Formats:host:portfor IPv4,[host]:portfor IPv6 (e.g.,['[::1]:8080']).IPv6 needs Perl 5.14+ with Socket IPv6 support; no other option is required. A bare (unbracketed) IPv6 address whose tail looks like a port is rejected as ambiguous - use the bracket form.
An entry beginning with
/or.is bound as a UNIX-domain socket instead of TCP. Such a socket is created world-accessible (mode0777) so that a reverse proxy running as another user can connect; if that is too permissive, place it in a directory whose permissions restrict who can reach it, which is the usual way to control access to a UNIX socket.Alternatively, use
hostandportparameters. - pre_fork
-
Fork this many worker processes.
By default the app is loaded once in the parent and workers inherit it via fork (copy-on-write friendly). Set
preload_app => 0to load the app independently in each worker instead. - preload_app
-
Controls whether the app is loaded before or after forking workers (default: true / load before fork).
When true (default), the app and all its modules are loaded once in the parent process. Workers inherit the loaded code via fork, benefiting from OS copy-on-write memory sharing. Use
after_forkto reconnect per-process resources (database handles, etc.).When false, workers fork first and each loads the app independently. This is useful when the app has per-process initialization that cannot be deferred to
after_fork, or when you want to test that module loading works in the worker environment. Requiresapp_file.Not supported together with
hot_restart: each generation loads the app once in its supervisor and forks workers copy-on-write, sopreload_app => 0is ignored in that mode.Feersum::Runner->new( pre_fork => 4, preload_app => 0, app_file => 'app.psgi', after_fork => sub { ... }, )->run; - hot_restart
-
Enable generation-based hot restart. Requires
app_file.The entry process becomes a supervisor that manages "generations". Each generation is a forked child that re-runs the app file, loading the app and the modules it pulls in from scratch. On
SIGHUP, a new generation is forked; if it starts successfully, the old generation is gracefully shut down viaSIGQUIT. Failed restarts are rolled back (old generation continues serving).Works with
pre_fork: each generation forks its own workers. The app and the modules it loads are picked up fresh in each generation (the app file is re-run viadoin a new fork, not re-done in a single long-lived process). Modules already loaded by the supervisor beforerun()are inherited via fork and not reloaded -- restart the supervisor to refresh those. Workers within a generation always inherit the app copy-on-write (preload_app => 0is ignored in this mode).TLS (
tls, or the flattls_cert_file/tls_key_file) and HTTP/2 (h2) configuration is propagated to every generation, so hot restart works with HTTPS and H2.Note that under
plackupthe.psgipath must also be given to plackup itself (positionally or via-a) --app_fileonly tells Feersum what to re-run per generation, and plackup otherwise tries to load its own default app.psgi.plackup -s Feersum --app-file=app.psgi --hot-restart=1 --pre-fork=4 app.psgi kill -HUP <master-pid> # zero-downtime restart, reloads app code - backlog
-
Listen socket backlog size (default:
SOMAXCONN). Set to a higher value (e.g. 65535) if the kernel'ssomaxconnis tuned above the compile-timeSOMAXCONNconstant. The kernel silently clamps to its own maximum. - keepalive
-
Enable/disable http keepalive requests.
- reverse_proxy
-
Enable reverse proxy mode. When enabled, Feersum trusts X-Forwarded-For and X-Forwarded-Proto headers from upstream proxies:
REMOTE_ADDR is set to the first IP in X-Forwarded-For (the original client)
psgi.url_scheme is set from X-Forwarded-Proto (http or https)
For the native interface, use
$req->client_addressand$req->url_schemeto get the forwarded values (these respect reverse_proxy mode automatically).Only enable this when Feersum is behind a trusted proxy that sets these headers.
- proxy_protocol
-
Enable PROXY protocol support (HAProxy protocol). When enabled, Feersum expects each new connection to begin with a PROXY protocol header before any HTTP data. Both v1 (text) and v2 (binary) formats are auto-detected and supported.
The PROXY protocol header provides the real client IP address when Feersum is behind a load balancer like HAProxy, AWS ELB/NLB, or nginx (with proxy_protocol).
When a valid PROXY header is received:
REMOTE_ADDR/REMOTE_PORT are updated to the source address from the header
For v1 UNKNOWN, v2 LOCAL, or v2 UNSPEC/AF_UNIX families, the original address is preserved
This option works independently of
reverse_proxy. When both are enabled, the PROXY protocol sets the base address, which can then be overridden by X-Forwarded-For headers if reverse_proxy is also enabled.Important: Only enable this when ALL connections come through a proxy that sends PROXY headers. Connections without valid PROXY headers will be rejected with HTTP 400.
Example HAProxy configuration:
backend feersum_backend mode http server feersum 127.0.0.1:5000 send-proxy-v2 - psgix_io
-
Enable or disable the
psgix.ioPSGI extension (default: enabled). When disabled, Feersum skips creatingpsgix.ioin the PSGI env hash, which avoids per-request overhead if your app never uses WebSocket upgrades or raw I/O. - read_timeout
-
Set read/keepalive timeout in seconds (default: 5). Must be positive (non-zero).
- header_timeout
-
Set maximum time (in seconds) to receive complete HTTP headers (Slowloris protection). Default is 10 seconds. Pass 0 to disable. Connections that don't complete headers within the timeout are closed.
- write_timeout
-
Set maximum time (in seconds) to complete writing a response. Default is 0 (disabled). When enabled, connections that stall during response writing are closed.
- max_connection_reqs
-
Set max requests per connection in case of keepalive - 0(default) for unlimited.
- max_accept_per_loop
-
Set max connections to accept per event loop cycle (default: 64). Lower values give more fair distribution across prefork workers sharing a listen socket. Higher values improve throughput under heavy load by reducing syscall overhead.
- max_connections
-
Set maximum concurrent connections (default: 10000; 0 disables the limit). When the limit is reached, Feersum first closes the oldest idle keep-alive connection to make room; if none are idle, the new connection is closed and accepting is paused on that listener until a slot frees. Provides protection against Slowloris-style DoS attacks.
- max_read_buf
-
Set max read buffer size per connection (default: 64 MiB, 67108864 bytes). This limits how large the read buffer can grow during header parsing and chunked body reception.
- max_body_len
-
Set max request body size (default: 64 MiB, 67108864 bytes). This limits Content-Length values and cumulative chunked body sizes.
- max_uri_len
-
Set max request URI length (default: 8192).
- wbuf_low_water
-
Set write buffer low-water mark in bytes (default: 0). Used with
poll_cb()on streaming responses: the callback fires when the buffer drains to or below this threshold. - read_priority
- write_priority
- accept_priority
-
Set libev I/O watcher priorities for read, write, and accept operations. Valid range is -2 (lowest) to +2 (highest), default is 0.
- tls
-
Enable TLS 1.3 on all listeners. Pass a hash reference with
cert_fileandkey_filepaths:Feersum::Runner->new( listen => ['0.0.0.0:8443'], tls => { cert_file => 'server.crt', key_file => 'server.key' }, app => $app, )->run;Requires Feersum to be compiled with TLS support (picotls submodule + Alien::OpenSSL). HTTP/2 is not enabled by default; pass
h2 => 1separately to enable it. - tls_cert_file
- tls_key_file
-
Flat alternatives to the
tlshash, useful with plackup's pass-through options:plackup -s Feersum --tls-cert-file=server.crt --tls-key-file=server.keyBoth must be specified together. If a
tlshash is also provided, it takes precedence and these are ignored. - h2
-
Enable HTTP/2 negotiation via ALPN on TLS listeners (default: off). Requires TLS to be enabled. When Alien::nghttp2 was available at build time and this option is set, HTTP/2 is negotiated alongside HTTP/1.1 during the TLS handshake.
Feersum::Runner->new( listen => ['0.0.0.0:8443'], tls => { cert_file => 'server.crt', key_file => 'server.key' }, h2 => 1, app => $app, )->run;If set without TLS enabled, a fatal error (croak) is raised.
- sni
-
SNI virtual hosting: an arrayref of
{ sni => $hostname, cert_file => $path, key_file => $path }hashes. Each entry adds a certificate for the given hostname. Requirestlsto be set first (provides the default cert); croaks if it is not.Feersum::Runner->new( listen => ['0.0.0.0:8443'], tls => { cert_file => 'default.crt', key_file => 'default.key' }, sni => [ { sni => 'example.com', cert_file => 'ex.crt', key_file => 'ex.key' }, { sni => 'other.com', cert_file => 'ot.crt', key_file => 'ot.key' }, ], app => $app, )->run; - reuseport
-
Enable SO_REUSEPORT for better prefork scaling (default: off). When enabled in combination with
pre_fork, each worker process creates its own socket bound to the same address. The kernel then distributes incoming connections across workers, eliminating accept() contention and improving multi-core scaling. Requires Linux 3.9+ or similar kernel support.Each worker binds for itself, which
user/groupon a port below 1024 makes impossible. Feersum probes the bind while dropping privileges and falls back to sharing the inherited listen socket, with a warning.Worker churn. A reuseport socket owns its own accept queue, so when a worker stops accepting (
max_requests_per_workerretirement,hot_restartreload, shutdown), whatever the kernel had already queued on that socket would be destroyed rather than redistributed - those clients completed their TCP handshake and would see a connection reset having sent nothing. Every reuseport worker therefore runs withset_drain_accept_queueenabled (see Feersum): before closing its listener it accepts what the kernel already queued and serves those connections as part of the graceful drain, andTCP_DEFER_ACCEPTstays off so no established connection is invisible to that drain. Measured with four parallel clients hammeringpre_fork => 2workers retiring every 30 requests, destroyed-connection resets went from 1.6% of connections to 0.02%; withoutreuseportthe listen socket outlives any one worker and the same churn loses nothing.What churn still costs with
reuseportis connect-time failures, not destroyed requests: between a worker closing its listener and its replacement binding, the reuseport group is one member short, and if every member is in that gap at once a new SYN has nowhere to land and is refused; separately, a handshake whose final ACK is still in flight when the listener closes is reset before the drain can see it. Both need pathological churn to matter - workers retiring many times a second - and shrink linearly with the retirement interval and sharply with worker count (measured at retirement every ~10ms: 2.9% refused atpre_fork => 2, 0.9% at 3; at a realisticmax_requests_per_workerin the thousands both are negligible). Linux 5.14+ can close both entirely at the kernel level withnet.ipv4.tcp_migrate_req=1, which migrates queued and half-open connections to a surviving group member instead of resetting them.Note:
reuseportis not required for IPv6; seelisten. - max_requests_per_worker
-
Maximum total requests a worker process will handle before gracefully recycling (default: 0 = unlimited). Effective with
pre_forkorhot_restart; the parent (or hot-restart supervisor) automatically forks a replacement worker. Useful for containing memory leaks in long-running applications.The limit is exact - it is enforced in XS alongside the request counter (see "max_requests_per_worker()" in Feersum), so a worker retires on the request that reaches it, however fast the traffic. A retiring worker is not treated as a crash, so it does not trigger the respawn backoff.
graceful_timeoutbounds the retiring worker's drain, exactly as it bounds a shutdown. It has to: the replacement is forked only once the retiring worker exits, so an unbounded drain leaves the slot empty for as long as one slow connection cares to hold it. - access_log
-
Code reference called after each response completes (native handler only). Receives
($method, $uri, $elapsed_seconds). Requests the server rejects before dispatch (malformed, over a limit, timed out) produce no line; seeaccess_login Feersum. For PSGI apps, use Plack::Middleware::AccessLog instead.access_log => sub { my ($method, $uri, $elapsed) = @_; warn sprintf "%s %s %.3fms\n", $method, $uri, $elapsed * 1000; }, - graceful_timeout
-
Seconds to wait for in-flight requests to complete during graceful shutdown before force-exiting (default: 5; 0 force-exits immediately). Also honors the
FEERSUM_GRACEFUL_TIMEOUTenvironment variable (option takes precedence). In prefork mode the parent allows an extra 2 seconds beyond this before force-exiting, so its workers have a chance to drain and exit first. It bounds amax_requests_per_workerretirement the same way.Force-exiting cuts off whatever is still in flight, mid-body. A response that needs longer than this to finish is truncated: the client has already been told a
Content-Lengthit will never receive, and sees a short read at a clean EOF (detectable, but a failed download). Raise it if you serve large bodies withsendfileor long streaming responses, or a restart will truncate every transfer still running after the deadline. Feersum itself imposes no deadline on the drain; this timeout is the only thing bounding it, so it is also what stops a stuck connection from blocking shutdown forever. - startup_timeout
-
Seconds to wait for a hot_restart generation to signal readiness before declaring it failed and rolling back (default: 10; 0 fails the restart immediately). Also caps how long a
daemonizeparent waits for the daemon to report that it got far enough to serve; there a timeout is treated as success, so that a slow-but-working start is never reported as a failure. - after_fork
-
Code reference called in each worker child immediately after fork, before entering the event loop. Use this to reconnect database handles, reseed PRNGs, or close inherited file descriptors:
after_fork => sub { $dbh = DBI->connect(...) }, - pid_file
-
Write the server PID to this file. Removed on clean shutdown.
- daemonize
-
Fork into background, redirect STDIN/STDOUT/STDERR to /dev/null, and call
setsid(). The PID file (if specified) is written with the daemon's PID. - user
- group
-
Drop privileges to this user/group after creating listen sockets but before loading the application. Allows binding to privileged ports as root.
Supplementary groups are always cleared. If
groupis omitted, the group is taken fromuser's passwd entry, souser => 'www-data'drops to that user's primary group rather than leaving the process in root's groups. The drop is verified and croaks if it did not take effect, so a configuration that cannot clear its groups fails loudly instead of running with them.Note: TLS certificate and key files are opened by the worker, which is already running as
user. A key readable only by root (the usual/etc/ssl/privatelayout) therefore has to be made readable byuser. - max_h2_concurrent_streams
-
Maximum concurrent HTTP/2 streams per connection (default: 100). Requires H2 support compiled in.
- quiet
-
Don't be so noisy. (default: on)
- app
-
An already-compiled native Feersum app code reference, as an alternative to passing it to
run(). Mutually exclusive with a per-worker load: whenappis set,app_fileis not re-read in the workers. - app_file
-
Load this filename as a native feersum app. Required for
hot_restart, which re-reads the file in each new generation. Withpreload_appoff and noapp, each worker loads it independently after the fork.
$runner->run($feersum_app)-
Run Feersum with the specified app code reference. Note that this is not a PSGI app, but a native Feersum app.
$runner->assign_request_handler($subref)-
For sub-classes to override, assigns an app handler. (e.g. Plack::Handler::Feersum). By default, this assigns a Feersum-native (and not PSGI) handler.
$runner->quit()-
Initiate a graceful shutdown. A signal handler for SIGQUIT will call this method.
AUTHOR
Jeremy Stashewsky, stash@cpan.org
COPYRIGHT AND LICENSE
Copyright (C) 2010 by Jeremy Stashewsky & Socialtext Inc.
This library is free software; you can redistribute it and/or modify it under the same terms as Perl itself, either Perl version 5.14 or, at your option, any later version of Perl 5 you may have available.