Newer Older
405 lines | 10.574kb
add files
Yuki Kimoto authored on 2014-03-26
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