add files
|
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 Cwd 'abs_path'; |
|
7 |
use File::Basename 'dirname'; |
|
8 |
use File::Spec::Functions 'catfile'; |
|
9 |
use Mojo::Server::Prefork; |
|
10 |
use Mojo::Util 'steady_time'; |
|
11 |
use POSIX 'setsid'; |
|
12 |
use Scalar::Util 'weaken'; |
|
13 | ||
14 |
sub run { |
|
15 |
my ($self, $path) = @_; |
|
16 | ||
17 |
# No Windows support |
|
18 |
_exit('Hypnotoad not available for Windows.') if $^O eq 'MSWin32'; |
|
19 | ||
20 |
# Remember application for later |
|
21 |
$ENV{HYPNOTOAD_APP} ||= abs_path $path; |
|
22 | ||
23 |
# This is a production server |
|
24 |
$ENV{MOJO_MODE} ||= 'production'; |
|
25 | ||
26 |
# Remember executable for later |
|
27 |
$ENV{HYPNOTOAD_EXE} ||= $0; |
|
28 |
$0 = $ENV{HYPNOTOAD_APP}; |
|
29 | ||
30 |
# Clean start |
|
31 |
die "Can't exec: $!" if !$ENV{HYPNOTOAD_REV}++ && !exec $ENV{HYPNOTOAD_EXE}; |
|
32 | ||
33 |
# Preload application and configure server |
|
34 |
my $prefork = $self->{prefork} = Mojo::Server::Prefork->new; |
|
35 |
my $app = $prefork->load_app($ENV{HYPNOTOAD_APP}); |
|
36 |
$self->_config($app); |
|
37 |
weaken $self; |
|
38 |
$prefork->on(wait => sub { $self->_manage }); |
|
39 |
$prefork->on(reap => sub { $self->_reap(pop) }); |
|
40 |
$prefork->on(finish => sub { $self->{finished} = 1 }); |
|
41 | ||
42 |
# Testing |
|
43 |
_exit('Everything looks good!') if $ENV{HYPNOTOAD_TEST}; |
|
44 | ||
45 |
# Stop running server |
|
46 |
$self->_stop if $ENV{HYPNOTOAD_STOP}; |
|
47 | ||
48 |
# Initiate hot deployment |
|
49 |
$self->_hot_deploy unless $ENV{HYPNOTOAD_PID}; |
|
50 | ||
51 |
# Daemonize as early as possible (but not for restarts) |
|
52 |
if (!$ENV{HYPNOTOAD_FOREGROUND} && $ENV{HYPNOTOAD_REV} < 3) { |
|
53 | ||
54 |
# Fork and kill parent |
|
55 |
die "Can't fork: $!" unless defined(my $pid = fork); |
|
56 |
exit 0 if $pid; |
|
57 |
setsid or die "Can't start a new session: $!"; |
|
58 | ||
59 |
# Close filehandles |
|
60 |
open STDIN, '</dev/null'; |
|
61 |
open STDOUT, '>/dev/null'; |
|
62 |
open STDERR, '>&STDOUT'; |
|
63 |
} |
|
64 | ||
65 |
# Start accepting connections |
|
66 |
local $SIG{USR2} = sub { $self->{upgrade} ||= steady_time }; |
|
67 |
$prefork->run; |
|
68 |
} |
|
69 | ||
70 |
sub _config { |
|
71 |
my ($self, $app) = @_; |
|
72 | ||
73 |
# Hypnotoad settings |
|
74 |
my $c = $app->config('hypnotoad') || {}; |
|
75 |
$self->{upgrade_timeout} = $c->{upgrade_timeout} || 60; |
|
76 | ||
77 |
# Prefork settings |
|
78 |
$ENV{MOJO_REVERSE_PROXY} = $c->{proxy} if defined $c->{proxy}; |
|
79 |
my $prefork = $self->{prefork}->listen($c->{listen} || ['http://*:8080']); |
|
80 |
my $file = catfile dirname($ENV{HYPNOTOAD_APP}), 'hypnotoad.pid'; |
|
81 |
$prefork->pid_file($c->{pid_file} || $file); |
|
82 |
$prefork->max_clients($c->{clients}) if $c->{clients}; |
|
83 |
$prefork->max_requests($c->{keep_alive_requests}) |
|
84 |
if $c->{keep_alive_requests}; |
|
85 |
defined $c->{$_} and $prefork->$_($c->{$_}) |
|
86 |
for qw(accept_interval accepts backlog graceful_timeout group), |
|
87 |
qw(heartbeat_interval heartbeat_timeout inactivity_timeout lock_file), |
|
88 |
qw(lock_timeout multi_accept user workers); |
|
89 |
} |
|
90 | ||
91 |
sub _exit { say shift and exit 0 } |
|
92 | ||
93 |
sub _hot_deploy { |
|
94 |
my $self = shift; |
|
95 | ||
96 |
# Make sure server is running |
|
97 |
return unless my $pid = $self->{prefork}->check_pid; |
|
98 | ||
99 |
# Start hot deployment |
|
100 |
kill 'USR2', $pid; |
|
101 |
_exit("Starting hot deployment for Hypnotoad server $pid."); |
|
102 |
} |
|
103 | ||
104 |
sub _manage { |
|
105 |
my $self = shift; |
|
106 | ||
107 |
# Upgraded |
|
108 |
my $log = $self->{prefork}->app->log; |
|
109 |
if ($ENV{HYPNOTOAD_PID} && $ENV{HYPNOTOAD_PID} ne $$) { |
|
110 |
$log->info("Upgrade successful, stopping $ENV{HYPNOTOAD_PID}."); |
|
111 |
kill 'QUIT', $ENV{HYPNOTOAD_PID}; |
|
112 |
} |
|
113 |
$ENV{HYPNOTOAD_PID} = $$ unless ($ENV{HYPNOTOAD_PID} // '') eq $$; |
|
114 | ||
115 |
# Upgrade |
|
116 |
if ($self->{upgrade} && !$self->{finished}) { |
|
117 | ||
118 |
# Fresh start |
|
119 |
unless ($self->{new}) { |
|
120 |
$log->info('Starting zero downtime software upgrade.'); |
|
121 |
die "Can't fork: $!" unless defined(my $pid = $self->{new} = fork); |
|
122 |
exec($ENV{HYPNOTOAD_EXE}) or die("Can't exec: $!") unless $pid; |
|
123 |
} |
|
124 | ||
125 |
# Timeout |
|
126 |
kill 'KILL', $self->{new} |
|
127 |
if $self->{upgrade} + $self->{upgrade_timeout} <= steady_time; |
|
128 |
} |
|
129 |
} |
|
130 | ||
131 |
sub _reap { |
|
132 |
my ($self, $pid) = @_; |
|
133 | ||
134 |
# Clean up failed upgrade |
|
135 |
return unless ($self->{new} || '') eq $pid; |
|
136 |
$self->{prefork}->app->log->info('Zero downtime software upgrade failed.'); |
|
137 |
delete @$self{qw(new upgrade)}; |
|
138 |
} |
|
139 | ||
140 |
sub _stop { |
|
141 |
_exit('Hypnotoad server not running.') |
|
142 |
unless my $pid = shift->{prefork}->check_pid; |
|
143 |
kill 'QUIT', $pid; |
|
144 |
_exit("Stopping Hypnotoad server $pid gracefully."); |
|
145 |
} |
|
146 | ||
147 |
1; |
|
148 | ||
149 |
=encoding utf8 |
|
150 | ||
151 |
=head1 NAME |
|
152 | ||
153 |
Mojo::Server::Hypnotoad - ALL GLORY TO THE HYPNOTOAD! |
|
154 | ||
155 |
=head1 SYNOPSIS |
|
156 | ||
157 |
use Mojo::Server::Hypnotoad; |
|
158 | ||
159 |
my $toad = Mojo::Server::Hypnotoad->new; |
|
160 |
$toad->run('/home/sri/myapp.pl'); |
|
161 | ||
162 |
=head1 DESCRIPTION |
|
163 | ||
164 |
L<Mojo::Server::Hypnotoad> is a full featured, UNIX optimized, preforking |
|
165 |
non-blocking I/O HTTP and WebSocket server, built around the very well tested |
|
166 |
and reliable L<Mojo::Server::Prefork>, with IPv6, TLS, Comet (long polling), |
|
167 |
keep-alive, connection pooling, timeout, cookie, multipart, multiple event |
|
168 |
loop and hot deployment support that just works. Note that the server uses |
|
169 |
signals for process management, so you should avoid modifying signal handlers |
|
170 |
in your applications. |
|
171 | ||
172 |
To start applications with it you can use the L<hypnotoad> script, for |
|
173 |
L<Mojolicious> and L<Mojolicious::Lite> applications it will default to |
|
174 |
C<production> mode. |
|
175 | ||
176 |
$ hypnotoad myapp.pl |
|
177 |
Server available at http://127.0.0.1:8080. |
|
178 | ||
179 |
You can run the same command again for automatic hot deployment. |
|
180 | ||
181 |
$ hypnotoad myapp.pl |
|
182 |
Starting hot deployment for Hypnotoad server 31841. |
|
183 | ||
184 |
This second invocation will load the application again, detect the process id |
|
185 |
file with it, and send a L</"USR2"> signal to the already running server. |
|
186 | ||
187 |
For better scalability (epoll, kqueue) and to provide IPv6 as well as TLS |
|
188 |
support, the optional modules L<EV> (4.0+), L<IO::Socket::IP> (0.16+) and |
|
189 |
L<IO::Socket::SSL> (1.75+) will be used automatically by L<Mojo::IOLoop> if |
|
190 |
they are installed. Individual features can also be disabled with the |
|
191 |
MOJO_NO_IPV6 and MOJO_NO_TLS environment variables. |
|
192 | ||
193 |
See L<Mojolicious::Guides::Cookbook> for more. |
|
194 | ||
195 |
=head1 MANAGER SIGNALS |
|
196 | ||
197 |
The L<Mojo::Server::Hypnotoad> manager process can be controlled at runtime |
|
198 |
with the following signals. |
|
199 | ||
200 |
=head2 INT, TERM |
|
201 | ||
202 |
Shutdown server immediately. |
|
203 | ||
204 |
=head2 QUIT |
|
205 | ||
206 |
Shutdown server gracefully. |
|
207 | ||
208 |
=head2 TTIN |
|
209 | ||
210 |
Increase worker pool by one. |
|
211 | ||
212 |
=head2 TTOU |
|
213 | ||
214 |
Decrease worker pool by one. |
|
215 | ||
216 |
=head2 USR2 |
|
217 | ||
218 |
Attempt zero downtime software upgrade (hot deployment) without losing any |
|
219 |
incoming connections. |
|
220 | ||
221 |
Manager (old) |
|
222 |
|- Worker [1] |
|
223 |
|- Worker [2] |
|
224 |
|- Worker [3] |
|
225 |
|- Worker [4] |
|
226 |
+- Manager (new) |
|
227 |
|- Worker [1] |
|
228 |
|- Worker [2] |
|
229 |
|- Worker [3] |
|
230 |
+- Worker [4] |
|
231 | ||
232 |
The new manager will automatically send a L</"QUIT"> signal to the old manager |
|
233 |
and take over serving requests after starting up successfully. |
|
234 | ||
235 |
=head1 WORKER SIGNALS |
|
236 | ||
237 |
L<Mojo::Server::Hypnotoad> worker processes can be controlled at runtime with |
|
238 |
the following signals. |
|
239 | ||
240 |
=head2 INT, TERM |
|
241 | ||
242 |
Stop worker immediately. |
|
243 | ||
244 |
=head2 QUIT |
|
245 | ||
246 |
Stop worker gracefully. |
|
247 | ||
248 |
=head1 SETTINGS |
|
249 | ||
250 |
L<Mojo::Server::Hypnotoad> can be configured with the following settings, see |
|
251 |
L<Mojolicious::Guides::Cookbook/"Hypnotoad"> for examples. |
|
252 | ||
253 |
=head2 accept_interval |
|
254 | ||
255 |
accept_interval => 0.5 |
|
256 | ||
257 |
Interval in seconds for trying to reacquire the accept mutex, defaults to |
|
258 |
C<0.025>. Note that changing this value can affect performance and idle CPU |
|
259 |
usage. |
|
260 | ||
261 |
=head2 accepts |
|
262 | ||
263 |
accepts => 100 |
|
264 | ||
265 |
Maximum number of connections a worker is allowed to accept before stopping |
|
266 |
gracefully, defaults to C<1000>. Setting the value to C<0> will allow workers |
|
267 |
to accept new connections indefinitely. Note that up to half of this value can |
|
268 |
be subtracted randomly to improve load balancing, and that worker processes |
|
269 |
will stop sending heartbeat messages once the limit has been reached. |
|
270 | ||
271 |
=head2 backlog |
|
272 | ||
273 |
backlog => 128 |
|
274 | ||
275 |
Listen backlog size, defaults to C<SOMAXCONN>. |
|
276 | ||
277 |
=head2 clients |
|
278 | ||
279 |
clients => 100 |
|
280 | ||
281 |
Maximum number of parallel client connections per worker process, defaults to |
|
282 |
C<1000>. Note that depending on how much your application may block, you might |
|
283 |
want to decrease this value and increase L</"workers"> instead for better |
|
284 |
performance. |
|
285 | ||
286 |
=head2 graceful_timeout |
|
287 | ||
288 |
graceful_timeout => 15 |
|
289 | ||
290 |
Maximum amount of time in seconds stopping a worker gracefully may take before |
|
291 |
being forced, defaults to C<20>. |
|
292 | ||
293 |
=head2 group |
|
294 | ||
295 |
group => 'staff' |
|
296 | ||
297 |
Group name for worker processes. |
|
298 | ||
299 |
=head2 heartbeat_interval |
|
300 | ||
301 |
heartbeat_interval => 3 |
|
302 | ||
303 |
Heartbeat interval in seconds, defaults to C<5>. |
|
304 | ||
305 |
=head2 heartbeat_timeout |
|
306 | ||
307 |
heartbeat_timeout => 2 |
|
308 | ||
309 |
Maximum amount of time in seconds before a worker without a heartbeat will be |
|
310 |
stopped gracefully, defaults to C<20>. |
|
311 | ||
312 |
=head2 inactivity_timeout |
|
313 | ||
314 |
inactivity_timeout => 10 |
|
315 | ||
316 |
Maximum amount of time in seconds a connection can be inactive before getting |
|
317 |
closed, defaults to C<15>. Setting the value to C<0> will allow connections to |
|
318 |
be inactive indefinitely. |
|
319 | ||
320 |
=head2 keep_alive_requests |
|
321 | ||
322 |
keep_alive_requests => 50 |
|
323 | ||
324 |
Number of keep-alive requests per connection, defaults to C<25>. |
|
325 | ||
326 |
=head2 listen |
|
327 | ||
328 |
listen => ['http://*:80'] |
|
329 | ||
330 |
List of one or more locations to listen on, defaults to C<http://*:8080>. See |
|
331 |
also L<Mojo::Server::Daemon/"listen"> for more examples. |
|
332 | ||
333 |
=head2 lock_file |
|
334 | ||
335 |
lock_file => '/tmp/hypnotoad.lock' |
|
336 | ||
337 |
Full path of accept mutex lock file prefix, to which the process id will be |
|
338 |
appended, defaults to a random temporary path. |
|
339 | ||
340 |
=head2 lock_timeout |
|
341 | ||
342 |
lock_timeout => 0.5 |
|
343 | ||
344 |
Maximum amount of time in seconds a worker may block when waiting for the |
|
345 |
accept mutex, defaults to C<1>. Note that changing this value can affect |
|
346 |
performance and idle CPU usage. |
|
347 | ||
348 |
=head2 multi_accept |
|
349 | ||
350 |
multi_accept => 100 |
|
351 | ||
352 |
Number of connections to accept at once, defaults to C<50>. |
|
353 | ||
354 |
=head2 pid_file |
|
355 | ||
356 |
pid_file => '/var/run/hypnotoad.pid' |
|
357 | ||
358 |
Full path to process id file, defaults to C<hypnotoad.pid> in the same |
|
359 |
directory as the application. Note that this value can only be changed after |
|
360 |
the server has been stopped. |
|
361 | ||
362 |
=head2 proxy |
|
363 | ||
364 |
proxy => 1 |
|
365 | ||
366 |
Activate reverse proxy support, which allows for the C<X-Forwarded-For> and |
|
367 |
C<X-Forwarded-HTTPS> headers to be picked up automatically, defaults to the |
|
368 |
value of the MOJO_REVERSE_PROXY environment variable. |
|
369 | ||
370 |
=head2 upgrade_timeout |
|
371 | ||
372 |
upgrade_timeout => 45 |
|
373 | ||
374 |
Maximum amount of time in seconds a zero downtime software upgrade may take |
|
375 |
before getting canceled, defaults to C<60>. |
|
376 | ||
377 |
=head2 user |
|
378 | ||
379 |
user => 'sri' |
|
380 | ||
381 |
Username for worker processes. |
|
382 | ||
383 |
=head2 workers |
|
384 | ||
385 |
workers => 10 |
|
386 | ||
387 |
Number of worker processes, defaults to C<4>. A good rule of thumb is two |
|
388 |
worker processes per CPU core. |
|
389 | ||
390 |
=head1 METHODS |
|
391 | ||
392 |
L<Mojo::Server::Hypnotoad> inherits all methods from L<Mojo::Base> and |
|
393 |
implements the following new ones. |
|
394 | ||
395 |
=head2 run |
|
396 | ||
397 |
$toad->run('script/myapp'); |
|
398 | ||
399 |
Run server for application. |
|
400 | ||
401 |
=head1 SEE ALSO |
|
402 | ||
403 |
L<Mojolicious>, L<Mojolicious::Guides>, L<http://mojolicio.us>. |
|
404 | ||
405 |
=cut |