Initial Commit
This commit is contained in:
389
database/perl/vendor/lib/Mojo/Server/Hypnotoad.pm
vendored
Normal file
389
database/perl/vendor/lib/Mojo/Server/Hypnotoad.pm
vendored
Normal file
@@ -0,0 +1,389 @@
|
||||
package Mojo::Server::Hypnotoad;
|
||||
use Mojo::Base -base;
|
||||
|
||||
# "Bender: I was God once.
|
||||
# God: Yes, I saw. You were doing well, until everyone died."
|
||||
use Config;
|
||||
use Mojo::File qw(path);
|
||||
use Mojo::Server::Prefork;
|
||||
use Mojo::Util qw(steady_time);
|
||||
use Scalar::Util qw(weaken);
|
||||
|
||||
has prefork => sub { Mojo::Server::Prefork->new(listen => ['http://*:8080']) };
|
||||
has upgrade_timeout => 180;
|
||||
|
||||
sub configure {
|
||||
my ($self, $name) = @_;
|
||||
|
||||
# Hypnotoad settings
|
||||
my $prefork = $self->prefork;
|
||||
my $c = $prefork->app->config($name) // {};
|
||||
$self->upgrade_timeout($c->{upgrade_timeout}) if $c->{upgrade_timeout};
|
||||
|
||||
# Pre-fork settings
|
||||
$prefork->reverse_proxy($c->{proxy}) if defined $c->{proxy};
|
||||
$prefork->max_clients($c->{clients}) if $c->{clients};
|
||||
$prefork->max_requests($c->{requests}) if $c->{requests};
|
||||
defined $c->{$_} and $prefork->$_($c->{$_})
|
||||
for qw(accepts backlog graceful_timeout heartbeat_interval heartbeat_timeout inactivity_timeout keep_alive_timeout),
|
||||
qw(listen pid_file spare workers);
|
||||
}
|
||||
|
||||
sub run {
|
||||
my ($self, $app) = @_;
|
||||
|
||||
# No fork emulation support
|
||||
_exit('Hypnotoad does not support fork emulation.') if $Config{d_pseudofork};
|
||||
|
||||
# Remember executable and application for later
|
||||
$ENV{HYPNOTOAD_EXE} ||= $0;
|
||||
$0 = $ENV{HYPNOTOAD_APP} ||= path($app)->to_abs->to_string;
|
||||
|
||||
# This is a production server
|
||||
$ENV{MOJO_MODE} ||= 'production';
|
||||
|
||||
# Clean start (to make sure everything works)
|
||||
die "Can't exec: $!" if !$ENV{HYPNOTOAD_REV}++ && !exec $^X, $ENV{HYPNOTOAD_EXE};
|
||||
|
||||
# Preload application and configure server
|
||||
my $prefork = $self->prefork->cleanup(0);
|
||||
$app = $prefork->load_app($app);
|
||||
$app->config->{hypnotoad}{pid_file} //= path($ENV{HYPNOTOAD_APP})->sibling('hypnotoad.pid')->to_string;
|
||||
$self->configure('hypnotoad');
|
||||
weaken $self;
|
||||
$prefork->on(wait => sub { $self->_manage });
|
||||
$prefork->on(reap => sub { $self->_cleanup(pop) });
|
||||
$prefork->on(finish => sub { $self->_finish });
|
||||
|
||||
# Testing
|
||||
_exit('Everything looks good!') if $ENV{HYPNOTOAD_TEST};
|
||||
|
||||
# Stop running server
|
||||
$self->_stop if $ENV{HYPNOTOAD_STOP};
|
||||
|
||||
# Initiate hot deployment
|
||||
$self->_hot_deploy unless $ENV{HYPNOTOAD_PID};
|
||||
|
||||
# Daemonize as early as possible (but not for restarts)
|
||||
local $SIG{USR2} = sub { $self->{upgrade} ||= steady_time };
|
||||
$prefork->start;
|
||||
$prefork->daemonize if !$ENV{HYPNOTOAD_FOREGROUND} && $ENV{HYPNOTOAD_REV} < 3;
|
||||
|
||||
# Start accepting connections
|
||||
$prefork->cleanup(1)->run;
|
||||
}
|
||||
|
||||
sub _cleanup {
|
||||
my ($self, $pid) = @_;
|
||||
|
||||
# Clean up failed upgrade
|
||||
return unless ($self->{new} || '') eq $pid;
|
||||
$self->prefork->app->log->error('Zero downtime software upgrade failed');
|
||||
delete @$self{qw(new upgrade)};
|
||||
}
|
||||
|
||||
sub _exit { say shift and exit 0 }
|
||||
|
||||
sub _finish {
|
||||
my $self = shift;
|
||||
|
||||
$self->{finish} = 1;
|
||||
return unless my $new = $self->{new};
|
||||
|
||||
my $prefork = $self->prefork->cleanup(0);
|
||||
path($prefork->pid_file)->remove;
|
||||
$prefork->ensure_pid_file($new);
|
||||
}
|
||||
|
||||
sub _hot_deploy {
|
||||
|
||||
# Make sure server is running
|
||||
return unless my $pid = shift->prefork->check_pid;
|
||||
|
||||
# Start hot deployment
|
||||
kill 'USR2', $pid;
|
||||
_exit("Starting hot deployment for Hypnotoad server $pid.");
|
||||
}
|
||||
|
||||
sub _manage {
|
||||
my $self = shift;
|
||||
|
||||
# Upgraded (wait for all workers to send a heartbeat)
|
||||
my $prefork = $self->prefork;
|
||||
my $log = $prefork->app->log;
|
||||
if ($ENV{HYPNOTOAD_PID} && $ENV{HYPNOTOAD_PID} ne $$) {
|
||||
return unless $prefork->healthy == $prefork->workers;
|
||||
$log->info("Upgrade successful, stopping $ENV{HYPNOTOAD_PID}");
|
||||
kill 'QUIT', $ENV{HYPNOTOAD_PID};
|
||||
}
|
||||
$ENV{HYPNOTOAD_PID} = $$ unless ($ENV{HYPNOTOAD_PID} // '') eq $$;
|
||||
|
||||
# Upgrade
|
||||
if ($self->{upgrade} && !$self->{finished}) {
|
||||
|
||||
# Fresh start
|
||||
my $ut = $self->upgrade_timeout;
|
||||
unless ($self->{new}) {
|
||||
$log->info("Starting zero downtime software upgrade ($ut seconds)");
|
||||
die "Can't fork: $!" unless defined(my $pid = $self->{new} = fork);
|
||||
exec $^X, $ENV{HYPNOTOAD_EXE} or die "Can't exec: $!" unless $pid;
|
||||
}
|
||||
|
||||
# Timeout
|
||||
kill 'KILL', $self->{new} if $self->{upgrade} + $ut <= steady_time;
|
||||
}
|
||||
}
|
||||
|
||||
sub _stop {
|
||||
_exit('Hypnotoad server not running.') unless my $pid = shift->prefork->check_pid;
|
||||
kill 'QUIT', $pid;
|
||||
_exit("Stopping Hypnotoad server $pid gracefully.");
|
||||
}
|
||||
|
||||
1;
|
||||
|
||||
=encoding utf8
|
||||
|
||||
=head1 NAME
|
||||
|
||||
Mojo::Server::Hypnotoad - A production web serv...ALL GLORY TO THE HYPNOTOAD!
|
||||
|
||||
=head1 SYNOPSIS
|
||||
|
||||
use Mojo::Server::Hypnotoad;
|
||||
|
||||
my $hypnotoad = Mojo::Server::Hypnotoad->new;
|
||||
$hypnotoad->run('/home/sri/myapp.pl');
|
||||
|
||||
=head1 DESCRIPTION
|
||||
|
||||
L<Mojo::Server::Hypnotoad> is a full featured, UNIX optimized, pre-forking non-blocking I/O HTTP and WebSocket server,
|
||||
built around the very well tested and reliable L<Mojo::Server::Prefork>, with IPv6, TLS, SNI, UNIX domain socket, Comet
|
||||
(long polling), keep-alive, multiple event loop and hot deployment support that just works. Note that the server uses
|
||||
signals for process management, so you should avoid modifying signal handlers in your applications.
|
||||
|
||||
To start applications with it you can use the L<hypnotoad> script, which listens on port C<8080>, automatically
|
||||
daemonizes the server process and defaults to C<production> mode for L<Mojolicious> and L<Mojolicious::Lite>
|
||||
applications.
|
||||
|
||||
$ hypnotoad ./myapp.pl
|
||||
|
||||
You can run the same command again for automatic hot deployment.
|
||||
|
||||
$ hypnotoad ./myapp.pl
|
||||
Starting hot deployment for Hypnotoad server 31841.
|
||||
|
||||
This second invocation will load the application again, detect the process id file with it, and send a L</"USR2">
|
||||
signal to the already running server.
|
||||
|
||||
For better scalability (epoll, kqueue) and to provide non-blocking name resolution, SOCKS5 as well as TLS support, the
|
||||
optional modules L<EV> (4.32+), L<Net::DNS::Native> (0.15+), L<IO::Socket::Socks> (0.64+) and L<IO::Socket::SSL>
|
||||
(2.009+) will be used automatically if possible. Individual features can also be disabled with the C<MOJO_NO_NNR>,
|
||||
C<MOJO_NO_SOCKS> and C<MOJO_NO_TLS> environment variables.
|
||||
|
||||
See L<Mojolicious::Guides::Cookbook/"DEPLOYMENT"> for more.
|
||||
|
||||
=head1 MANAGER SIGNALS
|
||||
|
||||
The L<Mojo::Server::Hypnotoad> manager process can be controlled at runtime with the following signals.
|
||||
|
||||
=head2 INT, TERM
|
||||
|
||||
Shut down server immediately.
|
||||
|
||||
=head2 QUIT
|
||||
|
||||
Shut down server gracefully.
|
||||
|
||||
=head2 TTIN
|
||||
|
||||
Increase worker pool by one.
|
||||
|
||||
=head2 TTOU
|
||||
|
||||
Decrease worker pool by one.
|
||||
|
||||
=head2 USR2
|
||||
|
||||
Attempt zero downtime software upgrade (hot deployment) without losing any incoming connections.
|
||||
|
||||
Manager (old)
|
||||
|- Worker [1]
|
||||
|- Worker [2]
|
||||
|- Worker [3]
|
||||
|- Worker [4]
|
||||
+- Manager (new)
|
||||
|- Worker [1]
|
||||
|- Worker [2]
|
||||
|- Worker [3]
|
||||
+- Worker [4]
|
||||
|
||||
The new manager will automatically send a L</"QUIT"> signal to the old manager and take over serving requests after
|
||||
starting up successfully.
|
||||
|
||||
=head1 WORKER SIGNALS
|
||||
|
||||
L<Mojo::Server::Hypnotoad> worker processes can be controlled at runtime with the following signals.
|
||||
|
||||
=head2 QUIT
|
||||
|
||||
Stop worker gracefully.
|
||||
|
||||
=head1 SETTINGS
|
||||
|
||||
L<Mojo::Server::Hypnotoad> can be configured with the following settings, see
|
||||
L<Mojolicious::Guides::Cookbook/"Hypnotoad"> for examples.
|
||||
|
||||
=head2 accepts
|
||||
|
||||
accepts => 100
|
||||
|
||||
Maximum number of connections a worker is allowed to accept, before stopping gracefully and then getting replaced with
|
||||
a newly started worker, defaults to the value of L<Mojo::Server::Prefork/"accepts">. Setting the value to C<0> will
|
||||
allow workers to accept new connections indefinitely. Note that up to half of this value can be subtracted randomly to
|
||||
improve load balancing, and to make sure that not all workers restart at the same time.
|
||||
|
||||
=head2 backlog
|
||||
|
||||
backlog => 128
|
||||
|
||||
Listen backlog size, defaults to the value of L<Mojo::Server::Daemon/"backlog">.
|
||||
|
||||
=head2 clients
|
||||
|
||||
clients => 100
|
||||
|
||||
Maximum number of accepted connections each worker process is allowed to handle concurrently, before stopping to accept
|
||||
new incoming connections, defaults to the value of L<Mojo::IOLoop/"max_connections">. Note that high concurrency works
|
||||
best with applications that perform mostly non-blocking operations, to optimize for blocking operations you can
|
||||
decrease this value and increase L</"workers"> instead for better performance.
|
||||
|
||||
=head2 graceful_timeout
|
||||
|
||||
graceful_timeout => 15
|
||||
|
||||
Maximum amount of time in seconds stopping a worker gracefully may take before being forced, defaults to the value of
|
||||
L<Mojo::Server::Prefork/"graceful_timeout">. Note that this value should usually be a little larger than the maximum
|
||||
amount of time you expect any one request to take.
|
||||
|
||||
=head2 heartbeat_interval
|
||||
|
||||
heartbeat_interval => 3
|
||||
|
||||
Heartbeat interval in seconds, defaults to the value of L<Mojo::Server::Prefork/"heartbeat_interval">.
|
||||
|
||||
=head2 heartbeat_timeout
|
||||
|
||||
heartbeat_timeout => 2
|
||||
|
||||
Maximum amount of time in seconds before a worker without a heartbeat will be stopped gracefully, defaults to the value
|
||||
of L<Mojo::Server::Prefork/"heartbeat_timeout">. Note that this value should usually be a little larger than the
|
||||
maximum amount of time you expect any one operation to block the event loop.
|
||||
|
||||
=head2 inactivity_timeout
|
||||
|
||||
inactivity_timeout => 10
|
||||
|
||||
Maximum amount of time in seconds a connection with an active request can be inactive before getting closed, defaults
|
||||
to the value of L<Mojo::Server::Daemon/"inactivity_timeout">. Setting the value to C<0> will allow connections to be
|
||||
inactive indefinitely.
|
||||
|
||||
=head2 keep_alive_timeout
|
||||
|
||||
keep_alive_timeout => 10
|
||||
|
||||
Maximum amount of time in seconds a connection without an active request can be inactive before getting closed,
|
||||
defaults to the value of L<Mojo::Server::Daemon/"keep_alive_timeout">. Setting the value to C<0> will allow connections
|
||||
to be inactive indefinitely.
|
||||
|
||||
=head2 listen
|
||||
|
||||
listen => ['http://*:80']
|
||||
|
||||
Array reference with one or more locations to listen on, defaults to C<http://*:8080>. See also
|
||||
L<Mojo::Server::Daemon/"listen"> for more examples.
|
||||
|
||||
=head2 pid_file
|
||||
|
||||
pid_file => '/var/run/hypnotoad.pid'
|
||||
|
||||
Full path to process id file, defaults to C<hypnotoad.pid> in the same directory as the application. Note that this
|
||||
value can only be changed after the server has been stopped.
|
||||
|
||||
=head2 proxy
|
||||
|
||||
proxy => 1
|
||||
|
||||
Activate reverse proxy support, which allows for the C<X-Forwarded-For> and C<X-Forwarded-Proto> headers to be picked
|
||||
up automatically, defaults to the value of L<Mojo::Server/"reverse_proxy">.
|
||||
|
||||
=head2 requests
|
||||
|
||||
requests => 50
|
||||
|
||||
Number of keep-alive requests per connection, defaults to the value of L<Mojo::Server::Daemon/"max_requests">.
|
||||
|
||||
=head2 spare
|
||||
|
||||
spare => 4
|
||||
|
||||
Temporarily spawn up to this number of additional workers if there is a need, defaults to the value of
|
||||
L<Mojo::Server::Prefork/"spare">. This allows for new workers to be started while old ones are still shutting down
|
||||
gracefully, drastically reducing the performance cost of worker restarts.
|
||||
|
||||
=head2 upgrade_timeout
|
||||
|
||||
upgrade_timeout => 45
|
||||
|
||||
Maximum amount of time in seconds a zero downtime software upgrade may take before getting canceled, defaults to
|
||||
C<180>.
|
||||
|
||||
=head2 workers
|
||||
|
||||
workers => 10
|
||||
|
||||
Number of worker processes, defaults to the value of L<Mojo::Server::Prefork/"workers">. A good rule of thumb is two
|
||||
worker processes per CPU core for applications that perform mostly non-blocking operations, blocking operations often
|
||||
require more and benefit from decreasing concurrency with L</"clients"> (often as low as C<1>). Note that during zero
|
||||
downtime software upgrades there will be twice as many workers active for a short amount of time.
|
||||
|
||||
=head1 ATTRIBUTES
|
||||
|
||||
L<Mojo::Server::Hypnotoad> implements the following attributes.
|
||||
|
||||
=head2 prefork
|
||||
|
||||
my $prefork = $hypnotoad->prefork;
|
||||
$hypnotoad = $hypnotoad->prefork(Mojo::Server::Prefork->new);
|
||||
|
||||
L<Mojo::Server::Prefork> object this server manages.
|
||||
|
||||
=head2 upgrade_timeout
|
||||
|
||||
my $timeout = $hypnotoad->upgrade_timeout;
|
||||
$hypnotoad = $hypnotoad->upgrade_timeout(15);
|
||||
|
||||
Maximum amount of time in seconds a zero downtime software upgrade may take before getting canceled, defaults to
|
||||
C<180>.
|
||||
|
||||
=head1 METHODS
|
||||
|
||||
L<Mojo::Server::Hypnotoad> inherits all methods from L<Mojo::Base> and implements the following new ones.
|
||||
|
||||
=head2 configure
|
||||
|
||||
$hypnotoad->configure('hypnotoad');
|
||||
|
||||
Configure server from application settings.
|
||||
|
||||
=head2 run
|
||||
|
||||
$hypnotoad->run('script/my_app');
|
||||
|
||||
Run server for application and wait for L</"MANAGER SIGNALS">.
|
||||
|
||||
=head1 SEE ALSO
|
||||
|
||||
L<Mojolicious>, L<Mojolicious::Guides>, L<https://mojolicious.org>.
|
||||
|
||||
=cut
|
||||
Reference in New Issue
Block a user