source: main/trunk/greenstone2/perllib/cpan/Mojo/Server/Hypnotoad.pm@ 32205

Last change on this file since 32205 was 32205, checked in by ak19, 6 years ago

First set of commits to do with implementing the new 'paged_html' output option of PDFPlugin that uses using xpdftools' new pdftohtml. So far tested only on Linux (64 bit), but things work there so I'm optimistically committing the changes since they work. 2. Committing the pre-built Linux binaries of XPDFtools for both 32 and 64 bit built by the XPDF group. 2. To use the correct bitness variant of xpdftools, setup.bash now exports the BITNESS env var, consulted by gsConvert.pl. 3. All the perl code changes to do with using xpdf tools' pdftohtml to generate paged_html and feed it in the desired form into GS(3): gsConvert.pl, PDFPlugin.pm and its parent ConvertBinaryPFile.pm have been modified to make it all work. xpdftools' pdftohtml generates a folder containing an html file and a screenshot for each page in a PDF (as well as an index.html linking to each page's html). However, we want a single html file that contains each individual 'page' html's content in a div, and need to do some further HTML style, attribute and structure modifications to massage the xpdftool output to what we want for GS. In order to parse and manipulate the HTML 'DOM' to do this, we're using the Mojo::DOM package that Dr Bainbridge found and which he's compiled up. Mojo::DOM is therefore also committed in this revision. Some further changes and some display fixes are required, but need to check with the others about that.

File size: 11.6 KB
Line 
1package Mojo::Server::Hypnotoad;
2use Mojo::Base -base;
3
4# "Bender: I was God once.
5# God: Yes, I saw. You were doing well, until everyone died."
6use Config;
7use Mojo::File 'path';
8use Mojo::Server::Prefork;
9use Mojo::Util 'steady_time';
10use Scalar::Util 'weaken';
11
12has prefork => sub { Mojo::Server::Prefork->new(listen => ['http://*:8080']) };
13has upgrade_timeout => 180;
14
15sub 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
32sub 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
77sub _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
86sub _exit { say shift and exit 0 }
87
88sub _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
99sub _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
109sub _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
138sub _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
1451;
146
147=encoding utf8
148
149=head1 NAME
150
151Mojo::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
162L<Mojo::Server::Hypnotoad> is a full featured, UNIX optimized, pre-forking
163non-blocking I/O HTTP and WebSocket server, built around the very well tested
164and reliable L<Mojo::Server::Prefork>, with IPv6, TLS, SNI, UNIX domain socket,
165Comet (long polling), keep-alive, multiple event loop and hot deployment support
166that just works. Note that the server uses signals for process management, so
167you should avoid modifying signal handlers in your applications.
168
169To start applications with it you can use the L<hypnotoad> script, which
170listens on port C<8080>, automatically daemonizes the server process and
171defaults to C<production> mode for L<Mojolicious> and L<Mojolicious::Lite>
172applications.
173
174 $ hypnotoad ./myapp.pl
175
176You can run the same command again for automatic hot deployment.
177
178 $ hypnotoad ./myapp.pl
179 Starting hot deployment for Hypnotoad server 31841.
180
181This second invocation will load the application again, detect the process id
182file with it, and send a L</"USR2"> signal to the already running server.
183
184For better scalability (epoll, kqueue) and to provide non-blocking name
185resolution, SOCKS5 as well as TLS support, the optional modules L<EV> (4.0+),
186L<Net::DNS::Native> (0.15+), L<IO::Socket::Socks> (0.64+) and
187L<IO::Socket::SSL> (2.009+) will be used automatically if possible. Individual
188features can also be disabled with the C<MOJO_NO_NNR>, C<MOJO_NO_SOCKS> and
189C<MOJO_NO_TLS> environment variables.
190
191See L<Mojolicious::Guides::Cookbook/"DEPLOYMENT"> for more.
192
193=head1 MANAGER SIGNALS
194
195The L<Mojo::Server::Hypnotoad> manager process can be controlled at runtime
196with the following signals.
197
198=head2 INT, TERM
199
200Shut down server immediately.
201
202=head2 QUIT
203
204Shut down server gracefully.
205
206=head2 TTIN
207
208Increase worker pool by one.
209
210=head2 TTOU
211
212Decrease worker pool by one.
213
214=head2 USR2
215
216Attempt zero downtime software upgrade (hot deployment) without losing any
217incoming 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
230The new manager will automatically send a L</"QUIT"> signal to the old manager
231and take over serving requests after starting up successfully.
232
233=head1 WORKER SIGNALS
234
235L<Mojo::Server::Hypnotoad> worker processes can be controlled at runtime with
236the following signals.
237
238=head2 QUIT
239
240Stop worker gracefully.
241
242=head1 SETTINGS
243
244L<Mojo::Server::Hypnotoad> can be configured with the following settings, see
245L<Mojolicious::Guides::Cookbook/"Hypnotoad"> for examples.
246
247=head2 accepts
248
249 accepts => 100
250
251Maximum number of connections a worker is allowed to accept, before stopping
252gracefully and then getting replaced with a newly started worker, defaults to
253the value of L<Mojo::Server::Prefork/"accepts">. Setting the value to C<0> will
254allow workers to accept new connections indefinitely. Note that up to half of
255this value can be subtracted randomly to improve load balancing, and to make
256sure that not all workers restart at the same time.
257
258=head2 backlog
259
260 backlog => 128
261
262Listen backlog size, defaults to the value of
263L<Mojo::Server::Daemon/"backlog">.
264
265=head2 clients
266
267 clients => 100
268
269Maximum number of accepted connections each worker process is allowed to handle
270concurrently, before stopping to accept new incoming connections, defaults to
271the value of L<Mojo::IOLoop/"max_connections">. Note that high concurrency works
272best with applications that perform mostly non-blocking operations, to optimize
273for blocking operations you can decrease this value and increase L</"workers">
274instead for better performance.
275
276=head2 graceful_timeout
277
278 graceful_timeout => 15
279
280Maximum amount of time in seconds stopping a worker gracefully may take before
281being forced, defaults to the value of
282L<Mojo::Server::Prefork/"graceful_timeout">. Note that this value should usually
283be a little larger than the maximum amount of time you expect any one request to
284take.
285
286=head2 heartbeat_interval
287
288 heartbeat_interval => 3
289
290Heartbeat interval in seconds, defaults to the value of
291L<Mojo::Server::Prefork/"heartbeat_interval">.
292
293=head2 heartbeat_timeout
294
295 heartbeat_timeout => 2
296
297Maximum amount of time in seconds before a worker without a heartbeat will be
298stopped gracefully, defaults to the value of
299L<Mojo::Server::Prefork/"heartbeat_timeout">. Note that this value should
300usually be a little larger than the maximum amount of time you expect any one
301operation to block the event loop.
302
303=head2 inactivity_timeout
304
305 inactivity_timeout => 10
306
307Maximum amount of time in seconds a connection can be inactive before getting
308closed, defaults to the value of L<Mojo::Server::Daemon/"inactivity_timeout">.
309Setting the value to C<0> will allow connections to be inactive indefinitely.
310
311=head2 listen
312
313 listen => ['http://*:80']
314
315Array reference with one or more locations to listen on, defaults to
316C<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
322Full path to process id file, defaults to C<hypnotoad.pid> in the same
323directory as the application. Note that this value can only be changed after
324the server has been stopped.
325
326=head2 proxy
327
328 proxy => 1
329
330Activate reverse proxy support, which allows for the C<X-Forwarded-For> and
331C<X-Forwarded-Proto> headers to be picked up automatically, defaults to the
332value of L<Mojo::Server/"reverse_proxy">.
333
334=head2 requests
335
336 requests => 50
337
338Number of keep-alive requests per connection, defaults to the value of
339L<Mojo::Server::Daemon/"max_requests">.
340
341=head2 spare
342
343 spare => 4
344
345Temporarily spawn up to this number of additional workers if there is a need,
346defaults to the value of L<Mojo::Server::Prefork/"spare">. This allows for new
347workers to be started while old ones are still shutting down gracefully,
348drastically reducing the performance cost of worker restarts.
349
350=head2 upgrade_timeout
351
352 upgrade_timeout => 45
353
354Maximum amount of time in seconds a zero downtime software upgrade may take
355before getting canceled, defaults to C<180>.
356
357=head2 workers
358
359 workers => 10
360
361Number of worker processes, defaults to the value of
362L<Mojo::Server::Prefork/"workers">. A good rule of thumb is two worker
363processes per CPU core for applications that perform mostly non-blocking
364operations, blocking operations often require more and benefit from decreasing
365concurrency with L</"clients"> (often as low as C<1>). Note that during zero
366downtime software upgrades there will be twice as many workers active for a
367short amount of time.
368
369=head1 ATTRIBUTES
370
371L<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
378L<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
385Maximum amount of time in seconds a zero downtime software upgrade may take
386before getting canceled, defaults to C<180>.
387
388=head1 METHODS
389
390L<Mojo::Server::Hypnotoad> inherits all methods from L<Mojo::Base> and
391implements the following new ones.
392
393=head2 configure
394
395 $hypnotoad->configure('hypnotoad');
396
397Configure server from application settings.
398
399=head2 run
400
401 $hypnotoad->run('script/my_app');
402
403Run server for application and wait for L</"MANAGER SIGNALS">.
404
405=head1 SEE ALSO
406
407L<Mojolicious>, L<Mojolicious::Guides>, L<https://mojolicious.org>.
408
409=cut
Note: See TracBrowser for help on using the repository browser.