1 | package Mojo::Server::Hypnotoad;
|
---|
2 | use Mojo::Base -base;
|
---|
3 |
|
---|
4 | # "Bender: I was God once.
|
---|
5 | # God: Yes, I saw. You were doing well, until everyone died."
|
---|
6 | use Config;
|
---|
7 | use Mojo::File 'path';
|
---|
8 | use Mojo::Server::Prefork;
|
---|
9 | use Mojo::Util 'steady_time';
|
---|
10 | use Scalar::Util 'weaken';
|
---|
11 |
|
---|
12 | has prefork => sub { Mojo::Server::Prefork->new(listen => ['http://*:8080']) };
|
---|
13 | has upgrade_timeout => 180;
|
---|
14 |
|
---|
15 | sub configure {
|
---|
16 | my ($self, $name) = @_;
|
---|
17 |
|
---|
18 | # Hypnotoad settings
|
---|
19 | my $prefork = $self->prefork;
|
---|
20 | my $c = $prefork->app->config($name) || {};
|
---|
21 | $self->upgrade_timeout($c->{upgrade_timeout}) if $c->{upgrade_timeout};
|
---|
22 |
|
---|
23 | # Pre-fork settings
|
---|
24 | $prefork->reverse_proxy($c->{proxy}) if defined $c->{proxy};
|
---|
25 | $prefork->max_clients($c->{clients}) if $c->{clients};
|
---|
26 | $prefork->max_requests($c->{requests}) if $c->{requests};
|
---|
27 | defined $c->{$_} and $prefork->$_($c->{$_})
|
---|
28 | for qw(accepts backlog graceful_timeout heartbeat_interval),
|
---|
29 | qw(heartbeat_timeout inactivity_timeout listen pid_file spare workers);
|
---|
30 | }
|
---|
31 |
|
---|
32 | sub run {
|
---|
33 | my ($self, $app) = @_;
|
---|
34 |
|
---|
35 | # No fork emulation support
|
---|
36 | _exit('Hypnotoad does not support fork emulation.') if $Config{d_pseudofork};
|
---|
37 |
|
---|
38 | # Remember executable and application for later
|
---|
39 | $ENV{HYPNOTOAD_EXE} ||= $0;
|
---|
40 | $0 = $ENV{HYPNOTOAD_APP} ||= path($app)->to_abs->to_string;
|
---|
41 |
|
---|
42 | # This is a production server
|
---|
43 | $ENV{MOJO_MODE} ||= 'production';
|
---|
44 |
|
---|
45 | # Clean start (to make sure everything works)
|
---|
46 | die "Can't exec: $!"
|
---|
47 | if !$ENV{HYPNOTOAD_REV}++ && !exec $^X, $ENV{HYPNOTOAD_EXE};
|
---|
48 |
|
---|
49 | # Preload application and configure server
|
---|
50 | my $prefork = $self->prefork->cleanup(0);
|
---|
51 | $prefork->load_app($app)->config->{hypnotoad}{pid_file}
|
---|
52 | //= path($ENV{HYPNOTOAD_APP})->sibling('hypnotoad.pid')->to_string;
|
---|
53 | $self->configure('hypnotoad');
|
---|
54 | weaken $self;
|
---|
55 | $prefork->on(wait => sub { $self->_manage });
|
---|
56 | $prefork->on(reap => sub { $self->_cleanup(pop) });
|
---|
57 | $prefork->on(finish => sub { $self->_finish });
|
---|
58 |
|
---|
59 | # Testing
|
---|
60 | _exit('Everything looks good!') if $ENV{HYPNOTOAD_TEST};
|
---|
61 |
|
---|
62 | # Stop running server
|
---|
63 | $self->_stop if $ENV{HYPNOTOAD_STOP};
|
---|
64 |
|
---|
65 | # Initiate hot deployment
|
---|
66 | $self->_hot_deploy unless $ENV{HYPNOTOAD_PID};
|
---|
67 |
|
---|
68 | # Daemonize as early as possible (but not for restarts)
|
---|
69 | local $SIG{USR2} = sub { $self->{upgrade} ||= steady_time };
|
---|
70 | $prefork->start;
|
---|
71 | $prefork->daemonize if !$ENV{HYPNOTOAD_FOREGROUND} && $ENV{HYPNOTOAD_REV} < 3;
|
---|
72 |
|
---|
73 | # Start accepting connections
|
---|
74 | $prefork->cleanup(1)->run;
|
---|
75 | }
|
---|
76 |
|
---|
77 | sub _cleanup {
|
---|
78 | my ($self, $pid) = @_;
|
---|
79 |
|
---|
80 | # Clean up failed upgrade
|
---|
81 | return unless ($self->{new} || '') eq $pid;
|
---|
82 | $self->prefork->app->log->error('Zero downtime software upgrade failed');
|
---|
83 | delete @$self{qw(new upgrade)};
|
---|
84 | }
|
---|
85 |
|
---|
86 | sub _exit { say shift and exit 0 }
|
---|
87 |
|
---|
88 | sub _finish {
|
---|
89 | my $self = shift;
|
---|
90 |
|
---|
91 | $self->{finish} = 1;
|
---|
92 | return unless my $new = $self->{new};
|
---|
93 |
|
---|
94 | my $prefork = $self->prefork->cleanup(0);
|
---|
95 | unlink $prefork->pid_file;
|
---|
96 | $prefork->ensure_pid_file($new);
|
---|
97 | }
|
---|
98 |
|
---|
99 | sub _hot_deploy {
|
---|
100 |
|
---|
101 | # Make sure server is running
|
---|
102 | return unless my $pid = shift->prefork->check_pid;
|
---|
103 |
|
---|
104 | # Start hot deployment
|
---|
105 | kill 'USR2', $pid;
|
---|
106 | _exit("Starting hot deployment for Hypnotoad server $pid.");
|
---|
107 | }
|
---|
108 |
|
---|
109 | sub _manage {
|
---|
110 | my $self = shift;
|
---|
111 |
|
---|
112 | # Upgraded (wait for all workers to send a heartbeat)
|
---|
113 | my $prefork = $self->prefork;
|
---|
114 | my $log = $prefork->app->log;
|
---|
115 | if ($ENV{HYPNOTOAD_PID} && $ENV{HYPNOTOAD_PID} ne $$) {
|
---|
116 | return unless $prefork->healthy == $prefork->workers;
|
---|
117 | $log->info("Upgrade successful, stopping $ENV{HYPNOTOAD_PID}");
|
---|
118 | kill 'QUIT', $ENV{HYPNOTOAD_PID};
|
---|
119 | }
|
---|
120 | $ENV{HYPNOTOAD_PID} = $$ unless ($ENV{HYPNOTOAD_PID} // '') eq $$;
|
---|
121 |
|
---|
122 | # Upgrade
|
---|
123 | if ($self->{upgrade} && !$self->{finished}) {
|
---|
124 |
|
---|
125 | # Fresh start
|
---|
126 | my $ut = $self->upgrade_timeout;
|
---|
127 | unless ($self->{new}) {
|
---|
128 | $log->info("Starting zero downtime software upgrade ($ut seconds)");
|
---|
129 | die "Can't fork: $!" unless defined(my $pid = $self->{new} = fork);
|
---|
130 | exec $^X, $ENV{HYPNOTOAD_EXE} or die "Can't exec: $!" unless $pid;
|
---|
131 | }
|
---|
132 |
|
---|
133 | # Timeout
|
---|
134 | kill 'KILL', $self->{new} if $self->{upgrade} + $ut <= steady_time;
|
---|
135 | }
|
---|
136 | }
|
---|
137 |
|
---|
138 | sub _stop {
|
---|
139 | _exit('Hypnotoad server not running.')
|
---|
140 | unless my $pid = shift->prefork->check_pid;
|
---|
141 | kill 'QUIT', $pid;
|
---|
142 | _exit("Stopping Hypnotoad server $pid gracefully.");
|
---|
143 | }
|
---|
144 |
|
---|
145 | 1;
|
---|
146 |
|
---|
147 | =encoding utf8
|
---|
148 |
|
---|
149 | =head1 NAME
|
---|
150 |
|
---|
151 | Mojo::Server::Hypnotoad - A production web serv...ALL GLORY TO THE HYPNOTOAD!
|
---|
152 |
|
---|
153 | =head1 SYNOPSIS
|
---|
154 |
|
---|
155 | use Mojo::Server::Hypnotoad;
|
---|
156 |
|
---|
157 | my $hypnotoad = Mojo::Server::Hypnotoad->new;
|
---|
158 | $hypnotoad->run('/home/sri/myapp.pl');
|
---|
159 |
|
---|
160 | =head1 DESCRIPTION
|
---|
161 |
|
---|
162 | L<Mojo::Server::Hypnotoad> is a full featured, UNIX optimized, pre-forking
|
---|
163 | non-blocking I/O HTTP and WebSocket server, built around the very well tested
|
---|
164 | and reliable L<Mojo::Server::Prefork>, with IPv6, TLS, SNI, UNIX domain socket,
|
---|
165 | Comet (long polling), keep-alive, multiple event loop and hot deployment support
|
---|
166 | that just works. Note that the server uses signals for process management, so
|
---|
167 | you should avoid modifying signal handlers in your applications.
|
---|
168 |
|
---|
169 | To start applications with it you can use the L<hypnotoad> script, which
|
---|
170 | listens on port C<8080>, automatically daemonizes the server process and
|
---|
171 | defaults to C<production> mode for L<Mojolicious> and L<Mojolicious::Lite>
|
---|
172 | applications.
|
---|
173 |
|
---|
174 | $ hypnotoad ./myapp.pl
|
---|
175 |
|
---|
176 | You can run the same command again for automatic hot deployment.
|
---|
177 |
|
---|
178 | $ hypnotoad ./myapp.pl
|
---|
179 | Starting hot deployment for Hypnotoad server 31841.
|
---|
180 |
|
---|
181 | This second invocation will load the application again, detect the process id
|
---|
182 | file with it, and send a L</"USR2"> signal to the already running server.
|
---|
183 |
|
---|
184 | For better scalability (epoll, kqueue) and to provide non-blocking name
|
---|
185 | resolution, SOCKS5 as well as TLS support, the optional modules L<EV> (4.0+),
|
---|
186 | L<Net::DNS::Native> (0.15+), L<IO::Socket::Socks> (0.64+) and
|
---|
187 | L<IO::Socket::SSL> (2.009+) will be used automatically if possible. Individual
|
---|
188 | features can also be disabled with the C<MOJO_NO_NNR>, C<MOJO_NO_SOCKS> and
|
---|
189 | C<MOJO_NO_TLS> environment variables.
|
---|
190 |
|
---|
191 | See L<Mojolicious::Guides::Cookbook/"DEPLOYMENT"> for more.
|
---|
192 |
|
---|
193 | =head1 MANAGER SIGNALS
|
---|
194 |
|
---|
195 | The L<Mojo::Server::Hypnotoad> manager process can be controlled at runtime
|
---|
196 | with the following signals.
|
---|
197 |
|
---|
198 | =head2 INT, TERM
|
---|
199 |
|
---|
200 | Shut down server immediately.
|
---|
201 |
|
---|
202 | =head2 QUIT
|
---|
203 |
|
---|
204 | Shut down server gracefully.
|
---|
205 |
|
---|
206 | =head2 TTIN
|
---|
207 |
|
---|
208 | Increase worker pool by one.
|
---|
209 |
|
---|
210 | =head2 TTOU
|
---|
211 |
|
---|
212 | Decrease worker pool by one.
|
---|
213 |
|
---|
214 | =head2 USR2
|
---|
215 |
|
---|
216 | Attempt zero downtime software upgrade (hot deployment) without losing any
|
---|
217 | incoming connections.
|
---|
218 |
|
---|
219 | Manager (old)
|
---|
220 | |- Worker [1]
|
---|
221 | |- Worker [2]
|
---|
222 | |- Worker [3]
|
---|
223 | |- Worker [4]
|
---|
224 | +- Manager (new)
|
---|
225 | |- Worker [1]
|
---|
226 | |- Worker [2]
|
---|
227 | |- Worker [3]
|
---|
228 | +- Worker [4]
|
---|
229 |
|
---|
230 | The new manager will automatically send a L</"QUIT"> signal to the old manager
|
---|
231 | and take over serving requests after starting up successfully.
|
---|
232 |
|
---|
233 | =head1 WORKER SIGNALS
|
---|
234 |
|
---|
235 | L<Mojo::Server::Hypnotoad> worker processes can be controlled at runtime with
|
---|
236 | the following signals.
|
---|
237 |
|
---|
238 | =head2 QUIT
|
---|
239 |
|
---|
240 | Stop worker gracefully.
|
---|
241 |
|
---|
242 | =head1 SETTINGS
|
---|
243 |
|
---|
244 | L<Mojo::Server::Hypnotoad> can be configured with the following settings, see
|
---|
245 | L<Mojolicious::Guides::Cookbook/"Hypnotoad"> for examples.
|
---|
246 |
|
---|
247 | =head2 accepts
|
---|
248 |
|
---|
249 | accepts => 100
|
---|
250 |
|
---|
251 | Maximum number of connections a worker is allowed to accept, before stopping
|
---|
252 | gracefully and then getting replaced with a newly started worker, defaults to
|
---|
253 | the value of L<Mojo::Server::Prefork/"accepts">. Setting the value to C<0> will
|
---|
254 | allow workers to accept new connections indefinitely. Note that up to half of
|
---|
255 | this value can be subtracted randomly to improve load balancing, and to make
|
---|
256 | sure that not all workers restart at the same time.
|
---|
257 |
|
---|
258 | =head2 backlog
|
---|
259 |
|
---|
260 | backlog => 128
|
---|
261 |
|
---|
262 | Listen backlog size, defaults to the value of
|
---|
263 | L<Mojo::Server::Daemon/"backlog">.
|
---|
264 |
|
---|
265 | =head2 clients
|
---|
266 |
|
---|
267 | clients => 100
|
---|
268 |
|
---|
269 | Maximum number of accepted connections each worker process is allowed to handle
|
---|
270 | concurrently, before stopping to accept new incoming connections, defaults to
|
---|
271 | the value of L<Mojo::IOLoop/"max_connections">. Note that high concurrency works
|
---|
272 | best with applications that perform mostly non-blocking operations, to optimize
|
---|
273 | for blocking operations you can decrease this value and increase L</"workers">
|
---|
274 | instead for better performance.
|
---|
275 |
|
---|
276 | =head2 graceful_timeout
|
---|
277 |
|
---|
278 | graceful_timeout => 15
|
---|
279 |
|
---|
280 | Maximum amount of time in seconds stopping a worker gracefully may take before
|
---|
281 | being forced, defaults to the value of
|
---|
282 | L<Mojo::Server::Prefork/"graceful_timeout">. Note that this value should usually
|
---|
283 | be a little larger than the maximum amount of time you expect any one request to
|
---|
284 | take.
|
---|
285 |
|
---|
286 | =head2 heartbeat_interval
|
---|
287 |
|
---|
288 | heartbeat_interval => 3
|
---|
289 |
|
---|
290 | Heartbeat interval in seconds, defaults to the value of
|
---|
291 | L<Mojo::Server::Prefork/"heartbeat_interval">.
|
---|
292 |
|
---|
293 | =head2 heartbeat_timeout
|
---|
294 |
|
---|
295 | heartbeat_timeout => 2
|
---|
296 |
|
---|
297 | Maximum amount of time in seconds before a worker without a heartbeat will be
|
---|
298 | stopped gracefully, defaults to the value of
|
---|
299 | L<Mojo::Server::Prefork/"heartbeat_timeout">. Note that this value should
|
---|
300 | usually be a little larger than the maximum amount of time you expect any one
|
---|
301 | operation to block the event loop.
|
---|
302 |
|
---|
303 | =head2 inactivity_timeout
|
---|
304 |
|
---|
305 | inactivity_timeout => 10
|
---|
306 |
|
---|
307 | Maximum amount of time in seconds a connection can be inactive before getting
|
---|
308 | closed, defaults to the value of L<Mojo::Server::Daemon/"inactivity_timeout">.
|
---|
309 | Setting the value to C<0> will allow connections to be inactive indefinitely.
|
---|
310 |
|
---|
311 | =head2 listen
|
---|
312 |
|
---|
313 | listen => ['http://*:80']
|
---|
314 |
|
---|
315 | Array reference with one or more locations to listen on, defaults to
|
---|
316 | C<http://*:8080>. See also L<Mojo::Server::Daemon/"listen"> for more examples.
|
---|
317 |
|
---|
318 | =head2 pid_file
|
---|
319 |
|
---|
320 | pid_file => '/var/run/hypnotoad.pid'
|
---|
321 |
|
---|
322 | Full path to process id file, defaults to C<hypnotoad.pid> in the same
|
---|
323 | directory as the application. Note that this value can only be changed after
|
---|
324 | the server has been stopped.
|
---|
325 |
|
---|
326 | =head2 proxy
|
---|
327 |
|
---|
328 | proxy => 1
|
---|
329 |
|
---|
330 | Activate reverse proxy support, which allows for the C<X-Forwarded-For> and
|
---|
331 | C<X-Forwarded-Proto> headers to be picked up automatically, defaults to the
|
---|
332 | value of L<Mojo::Server/"reverse_proxy">.
|
---|
333 |
|
---|
334 | =head2 requests
|
---|
335 |
|
---|
336 | requests => 50
|
---|
337 |
|
---|
338 | Number of keep-alive requests per connection, defaults to the value of
|
---|
339 | L<Mojo::Server::Daemon/"max_requests">.
|
---|
340 |
|
---|
341 | =head2 spare
|
---|
342 |
|
---|
343 | spare => 4
|
---|
344 |
|
---|
345 | Temporarily spawn up to this number of additional workers if there is a need,
|
---|
346 | defaults to the value of L<Mojo::Server::Prefork/"spare">. This allows for new
|
---|
347 | workers to be started while old ones are still shutting down gracefully,
|
---|
348 | drastically reducing the performance cost of worker restarts.
|
---|
349 |
|
---|
350 | =head2 upgrade_timeout
|
---|
351 |
|
---|
352 | upgrade_timeout => 45
|
---|
353 |
|
---|
354 | Maximum amount of time in seconds a zero downtime software upgrade may take
|
---|
355 | before getting canceled, defaults to C<180>.
|
---|
356 |
|
---|
357 | =head2 workers
|
---|
358 |
|
---|
359 | workers => 10
|
---|
360 |
|
---|
361 | Number of worker processes, defaults to the value of
|
---|
362 | L<Mojo::Server::Prefork/"workers">. A good rule of thumb is two worker
|
---|
363 | processes per CPU core for applications that perform mostly non-blocking
|
---|
364 | operations, blocking operations often require more and benefit from decreasing
|
---|
365 | concurrency with L</"clients"> (often as low as C<1>). Note that during zero
|
---|
366 | downtime software upgrades there will be twice as many workers active for a
|
---|
367 | short amount of time.
|
---|
368 |
|
---|
369 | =head1 ATTRIBUTES
|
---|
370 |
|
---|
371 | L<Mojo::Server::Hypnotoad> implements the following attributes.
|
---|
372 |
|
---|
373 | =head2 prefork
|
---|
374 |
|
---|
375 | my $prefork = $hypnotoad->prefork;
|
---|
376 | $hypnotoad = $hypnotoad->prefork(Mojo::Server::Prefork->new);
|
---|
377 |
|
---|
378 | L<Mojo::Server::Prefork> object this server manages.
|
---|
379 |
|
---|
380 | =head2 upgrade_timeout
|
---|
381 |
|
---|
382 | my $timeout = $hypnotoad->upgrade_timeout;
|
---|
383 | $hypnotoad = $hypnotoad->upgrade_timeout(15);
|
---|
384 |
|
---|
385 | Maximum amount of time in seconds a zero downtime software upgrade may take
|
---|
386 | before getting canceled, defaults to C<180>.
|
---|
387 |
|
---|
388 | =head1 METHODS
|
---|
389 |
|
---|
390 | L<Mojo::Server::Hypnotoad> inherits all methods from L<Mojo::Base> and
|
---|
391 | implements the following new ones.
|
---|
392 |
|
---|
393 | =head2 configure
|
---|
394 |
|
---|
395 | $hypnotoad->configure('hypnotoad');
|
---|
396 |
|
---|
397 | Configure server from application settings.
|
---|
398 |
|
---|
399 | =head2 run
|
---|
400 |
|
---|
401 | $hypnotoad->run('script/my_app');
|
---|
402 |
|
---|
403 | Run server for application and wait for L</"MANAGER SIGNALS">.
|
---|
404 |
|
---|
405 | =head1 SEE ALSO
|
---|
406 |
|
---|
407 | L<Mojolicious>, L<Mojolicious::Guides>, L<https://mojolicious.org>.
|
---|
408 |
|
---|
409 | =cut
|
---|