server-starter 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +17 -0
- data/CHANGELOG.md +4 -0
- data/Gemfile +3 -0
- data/LICENSE +22 -0
- data/README.md +103 -0
- data/Rakefile +18 -0
- data/bin/start_server.rb +292 -0
- data/example/Gemfile +6 -0
- data/example/config/unicorn.conf +46 -0
- data/example/config.ru +11 -0
- data/example/env/TEST +1 -0
- data/example/log/.gitkeep +0 -0
- data/example/restart_server +2 -0
- data/example/start_server +16 -0
- data/lib/server/starter/helper.rb +52 -0
- data/lib/server/starter/version.rb +5 -0
- data/lib/server/starter.rb +846 -0
- data/server-starter.gemspec +22 -0
- metadata +77 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 6d6ac01d0b1513979bb1b7d44518ad974250ac2e
|
4
|
+
data.tar.gz: 5b5cf5cc27f280472e5c3008127141b665cb56b9
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 2afe47513bde1c810d8c9d573cfb1f72c7a7cbbb62374afa4c378a73808d763161be574a5618e83a7d9628557608ffa3bb8e3bc49eb918725ca7af95d8b0520f
|
7
|
+
data.tar.gz: 7d384eb0e55be2d485f0739b323297ee69e610392769197750019ecf56300662458b17d450b445bfbe65a22353e494f2983b51190a9906ab3fd75f1a1464b4b6
|
data/.gitignore
ADDED
data/CHANGELOG.md
ADDED
data/Gemfile
ADDED
data/LICENSE
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2015 Naotoshi Seo
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,103 @@
|
|
1
|
+
# ruby-server-starter
|
2
|
+
|
3
|
+
a superdaemon for hot-deploying server programs (ruby port of [p5-Server-Starter](https://github.com/kazuho/p5-Server-Starter))
|
4
|
+
|
5
|
+
# Description
|
6
|
+
|
7
|
+
*note: this description is almost entirely taken from the original Server::Starter module*
|
8
|
+
|
9
|
+
The ```start_server``` utility is a superdaemon for hot-deploying server programs.
|
10
|
+
|
11
|
+
It is often a pain to write a server program that supports graceful restarts, with no resource leaks. Server::Starter solves the problem by splitting the task into two: ```start_server``` works as a superdaemon that binds to zero or more TCP ports or unix sockets, and repeatedly spawns the server program that actually handles the necessary tasks (for example, responding to incoming commenctions). The spawned server programs under ```start_server``` call accept(2) and handle the requests.
|
12
|
+
|
13
|
+
To gracefully restart the server program, send SIGHUP to the superdaemon. The superdaemon spawns a new server program, and if (and only if) it starts up successfully, sends SIGTERM to the old server program.
|
14
|
+
|
15
|
+
By using ```start_server``` it is much easier to write a hot-deployable server. Following are the only requirements a server program to be run under ```start_server``` should conform to:
|
16
|
+
|
17
|
+
- receive file descriptors to listen to through an environment variable
|
18
|
+
- perform a graceful shutdown when receiving SIGTERM
|
19
|
+
|
20
|
+
# Unicorn
|
21
|
+
|
22
|
+
Following is an example to run unicorn server under ```Server::Starter```.
|
23
|
+
|
24
|
+
The command line example:
|
25
|
+
|
26
|
+
```
|
27
|
+
bundle exec start_server.rb --status-file=/path/to/app/log/unicorn.stat \
|
28
|
+
--port=10080 --signal-on-hup=CONT --dir=/path/to/app -- \
|
29
|
+
bundle exec --keep-file-descriptors unicorn -c config/unicorn.conf.rb config.ru
|
30
|
+
```
|
31
|
+
|
32
|
+
An example of unicorn.conf:
|
33
|
+
|
34
|
+
```ruby
|
35
|
+
worker_processes 4
|
36
|
+
preload_app true
|
37
|
+
|
38
|
+
APP_ROOT = File.expand_path('../..', __FILE__)
|
39
|
+
status_file = File.join(APP_ROOT, 'log/unicorn.stat')
|
40
|
+
|
41
|
+
if ENV.key?('SERVER_STARTER_PORT')
|
42
|
+
fds = ENV['SERVER_STARTER_PORT'].split(';').map { |x|
|
43
|
+
path_or_port, fd = x.split('=', 2)
|
44
|
+
fd
|
45
|
+
}
|
46
|
+
ENV['UNICORN_FD'] = fds.join(',')
|
47
|
+
ENV.delete('SERVER_STARTER_PORT')
|
48
|
+
else
|
49
|
+
listen ENV['PORT'] || '10080'
|
50
|
+
end
|
51
|
+
|
52
|
+
before_fork do |server, worker|
|
53
|
+
defined?(ActiveRecord::Base) and ActiveRecord::Base.connection.disconnect!
|
54
|
+
|
55
|
+
# Throttle the master from forking too quickly by sleeping. Due
|
56
|
+
# to the implementation of standard Unix signal handlers, this
|
57
|
+
# helps (but does not completely) prevent identical, repeated signals
|
58
|
+
# from being lost when the receiving process is busy.
|
59
|
+
sleep 1
|
60
|
+
end
|
61
|
+
|
62
|
+
after_fork do |server, worker|
|
63
|
+
defined?(ActiveRecord::Base) and ActiveRecord::Base.establish_connection
|
64
|
+
|
65
|
+
begin
|
66
|
+
# This allows a new master process to incrementally
|
67
|
+
# phase out the old master process with SIGTTOU to avoid a
|
68
|
+
# thundering herd (especially in the "preload_app false" case)
|
69
|
+
# when doing a transparent upgrade. The last worker spawned
|
70
|
+
# will then kill off the old master process with a SIGQUIT.
|
71
|
+
pids = File.readlines(status_file).map {|_| _.chomp.split(':') }.to_h
|
72
|
+
old_gen = ENV['SERVER_STARTER_GENERATION'].to_i - 1
|
73
|
+
if old_pid = pids[old_gen.to_s]
|
74
|
+
sig = (worker.nr + 1) >= server.worker_processes ? :QUIT : :TTOU
|
75
|
+
Process.kill(sig, old_pid.to_i)
|
76
|
+
end
|
77
|
+
rescue Errno::ENOENT, Errno::ESRCH => e
|
78
|
+
$stderr.puts "#{e.class} #{e.message}"
|
79
|
+
end
|
80
|
+
end
|
81
|
+
```
|
82
|
+
|
83
|
+
## See Also
|
84
|
+
|
85
|
+
* [「Server::Starterに対応するとはどういうことか」の補足](http://blog.livedoor.jp/sonots/archives/40248661.html) (Japanese)
|
86
|
+
* [Server::Starter で Unicorn を起動する場合の Slow Restart](http://blog.livedoor.jp/sonots/archives/42826057.html) (Japanese)
|
87
|
+
|
88
|
+
## ChangeLog
|
89
|
+
|
90
|
+
See [CHANGELOG.md](CHANGELOG.md) for details.
|
91
|
+
|
92
|
+
## Contributing
|
93
|
+
|
94
|
+
1. Fork it
|
95
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
96
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
97
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
98
|
+
5. Create new [Pull Request](../../pull/new/master)
|
99
|
+
|
100
|
+
## Copyright
|
101
|
+
|
102
|
+
Copyright (c) 2015 Naotoshi Seo. See [LICENSE](LICENSE) for details.
|
103
|
+
|
data/Rakefile
ADDED
@@ -0,0 +1,18 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
require "bundler/gem_tasks"
|
3
|
+
|
4
|
+
require 'rake/testtask'
|
5
|
+
desc 'Run test_unit based test'
|
6
|
+
Rake::TestTask.new(:test) do |t|
|
7
|
+
t.libs << "test"
|
8
|
+
t.test_files = Dir["test/**/test_*.rb"].sort
|
9
|
+
t.verbose = true
|
10
|
+
#t.warning = true
|
11
|
+
end
|
12
|
+
task :default => :test
|
13
|
+
|
14
|
+
desc 'Open an irb session preloaded with the gem library'
|
15
|
+
task :console do
|
16
|
+
sh 'irb -rubygems -I lib'
|
17
|
+
end
|
18
|
+
task :c => :console
|
data/bin/start_server.rb
ADDED
@@ -0,0 +1,292 @@
|
|
1
|
+
#!/usr/bin/ruby
|
2
|
+
|
3
|
+
require 'optparse'
|
4
|
+
require_relative '../lib/server/starter'
|
5
|
+
|
6
|
+
opts = {
|
7
|
+
:port => [],
|
8
|
+
:path => [],
|
9
|
+
:interval => 1,
|
10
|
+
}
|
11
|
+
OptionParser.new do |opt|
|
12
|
+
opt.on(
|
13
|
+
'--port=(port|host:port),(port|host:port),...',
|
14
|
+
'TCP port to listen to (if omitted, will not bind to any ports)',
|
15
|
+
Array
|
16
|
+
) {|v| opts[:port] = v }
|
17
|
+
opt.on(
|
18
|
+
'--path=path,path,...',
|
19
|
+
'path at where to listen using unix socket (optional)',
|
20
|
+
Array,
|
21
|
+
) {|v| opts[:path] = v }
|
22
|
+
opt.on(
|
23
|
+
'--dir=path',
|
24
|
+
'working directory, start_server do chdir to before exec (optional)',
|
25
|
+
) {|v| opts[:dir] = v }
|
26
|
+
opt.on(
|
27
|
+
'--interval=seconds',
|
28
|
+
'minimum interval to respawn the server program (default: 1)',
|
29
|
+
) {|v| opts[:interval] = v.to_i }
|
30
|
+
opt.on(
|
31
|
+
'--signal-on-hup=SIGNAL',
|
32
|
+
'name of the signal to be sent to the server process when start_server receives a SIGHUP (default: SIGTERM). If you use this option, be sure to also use `--signal-on-term` below.',
|
33
|
+
) {|v| opts[:signal_on_hup] = v }
|
34
|
+
opt.on(
|
35
|
+
'--signal-on-term=SIGNAL',
|
36
|
+
'name of the signal to be sent to the server process when start_server receives a SIGTERM (default: SIGTERM)',
|
37
|
+
) {|v| opts[:signal_on_term] = v }
|
38
|
+
opt.on(
|
39
|
+
'--pid-file=filename',
|
40
|
+
'if set, writes the process id of the start_server process to the file',
|
41
|
+
) {|v| opts[:pid_file] = v }
|
42
|
+
opt.on(
|
43
|
+
'--status-file=filename',
|
44
|
+
'if set, writes the status of the server process(es) to the file',
|
45
|
+
) {|v| opts[:status_file] = v }
|
46
|
+
opt.on(
|
47
|
+
'--envdir=ENVDIR',
|
48
|
+
'directory that contains environment variables to the server processes. It is intended for use with `envdir` in `daemontools`. This can be overwritten by environment variable `ENVDIR`.',
|
49
|
+
) {|v| opts[:envdir] = v }
|
50
|
+
opt.on(
|
51
|
+
'--enable-auto-restart',
|
52
|
+
'enables automatic restart by time. This can be overwritten by environment variable `ENABLE_AUTO_RESTART`.',
|
53
|
+
) {|v| opts[:enable_auto_restart] = v }
|
54
|
+
opt.on(
|
55
|
+
'--auto-restart-interval=seconds',
|
56
|
+
'automatic restart interval (default 360). It is used with `--enable-auto-restart` option. This can be overwritten by environment variable `AUTO_RESTART_INTERVAL`.',
|
57
|
+
) {|v| opts[:auto_restart_interval] = v.to_i }
|
58
|
+
opt.on(
|
59
|
+
'--kill-old-delay=seconds',
|
60
|
+
'time to suspend to send a signal to the old worker. The default value is 5 when `--enable-auto-restart` is set, 0 otherwise. This can be overwritten by environment variable `KILL_OLD_DELAY`.'
|
61
|
+
) {|v| opts[:kill_old_delay] = v.to_i }
|
62
|
+
opt.on(
|
63
|
+
'--restart',
|
64
|
+
'this is a wrapper command that reads the pid of the start_server process from --pid-file, sends SIGHUP to the process and waits until the server(s) of the older generation(s) die by monitoring the contents of the --status-file'
|
65
|
+
) {|v| opts[:restart] = v }
|
66
|
+
opt.on(
|
67
|
+
'--backlog=num',
|
68
|
+
'specifies a listen backlog parameter, whose default is SOMAXCONN (usually 128 on Linux). While SOMAXCONN is enough for most loads, large backlog is required for heavy loads.',
|
69
|
+
) {|v| opts[:backlog] = v.to_i }
|
70
|
+
opt.on(
|
71
|
+
'--version',
|
72
|
+
'print version',
|
73
|
+
) {|v| puts Server::Starter::VERSION; exit 0 }
|
74
|
+
|
75
|
+
opt.parse!(ARGV)
|
76
|
+
end
|
77
|
+
|
78
|
+
starter = Server::Starter.new
|
79
|
+
|
80
|
+
if opts[:restart]
|
81
|
+
starter.restart_server(opts)
|
82
|
+
exit 0;
|
83
|
+
end
|
84
|
+
|
85
|
+
# validate options
|
86
|
+
if ARGV.empty?
|
87
|
+
$stderr.puts "server program not specified"
|
88
|
+
exit 1
|
89
|
+
end
|
90
|
+
|
91
|
+
starter.start_server(opts.merge({:exec => ARGV}))
|
92
|
+
|
93
|
+
__END__
|
94
|
+
#! /usr/bin/perl
|
95
|
+
|
96
|
+
use strict;
|
97
|
+
use warnings;
|
98
|
+
|
99
|
+
use Getopt::Long;
|
100
|
+
use Pod::Usage;
|
101
|
+
use Server::Starter qw(start_server restart_server);
|
102
|
+
|
103
|
+
my %opts = (
|
104
|
+
port => [],
|
105
|
+
path => [],
|
106
|
+
);
|
107
|
+
|
108
|
+
GetOptions(
|
109
|
+
map {
|
110
|
+
$_ => do {
|
111
|
+
my $name = (split '=', $_, 2)[0];
|
112
|
+
$name =~ s/-/_/g;
|
113
|
+
$opts{$name} ||= undef;
|
114
|
+
ref($opts{$name}) ? $opts{$name} : \$opts{$name};
|
115
|
+
},
|
116
|
+
} qw(port=s path=s interval=i log-file=s pid-file=s dir=s signal-on-hup=s signal-on-term=s
|
117
|
+
backlog=i envdir=s enable-auto-restart=i auto-restart-interval=i kill-old-delay=i
|
118
|
+
status-file=s restart help version),
|
119
|
+
) or exit 1;
|
120
|
+
pod2usage(
|
121
|
+
-exitval => 0,
|
122
|
+
-verbose => 1,
|
123
|
+
) if $opts{help};
|
124
|
+
if ($opts{version}) {
|
125
|
+
print "$Server::Starter::VERSION\n";
|
126
|
+
exit 0;
|
127
|
+
}
|
128
|
+
|
129
|
+
if ($opts{restart}) {
|
130
|
+
restart_server(%opts);
|
131
|
+
exit 0;
|
132
|
+
}
|
133
|
+
|
134
|
+
# validate options
|
135
|
+
die "server program not specified\n"
|
136
|
+
unless @ARGV;
|
137
|
+
|
138
|
+
start_server(
|
139
|
+
%opts,
|
140
|
+
exec => \@ARGV,
|
141
|
+
);
|
142
|
+
__END__
|
143
|
+
|
144
|
+
#! /usr/bin/perl
|
145
|
+
|
146
|
+
use strict;
|
147
|
+
use warnings;
|
148
|
+
|
149
|
+
use Getopt::Long;
|
150
|
+
use Pod::Usage;
|
151
|
+
use Server::Starter qw(start_server restart_server);
|
152
|
+
|
153
|
+
my %opts = (
|
154
|
+
port => [],
|
155
|
+
path => [],
|
156
|
+
);
|
157
|
+
|
158
|
+
GetOptions(
|
159
|
+
map {
|
160
|
+
$_ => do {
|
161
|
+
my $name = (split '=', $_, 2)[0];
|
162
|
+
$name =~ s/-/_/g;
|
163
|
+
$opts{$name} ||= undef;
|
164
|
+
ref($opts{$name}) ? $opts{$name} : \$opts{$name};
|
165
|
+
},
|
166
|
+
} qw(port=s path=s interval=i log-file=s pid-file=s dir=s signal-on-hup=s signal-on-term=s
|
167
|
+
backlog=i envdir=s enable-auto-restart=i auto-restart-interval=i kill-old-delay=i
|
168
|
+
status-file=s restart help version),
|
169
|
+
) or exit 1;
|
170
|
+
pod2usage(
|
171
|
+
-exitval => 0,
|
172
|
+
-verbose => 1,
|
173
|
+
) if $opts{help};
|
174
|
+
if ($opts{version}) {
|
175
|
+
print "$Server::Starter::VERSION\n";
|
176
|
+
exit 0;
|
177
|
+
}
|
178
|
+
|
179
|
+
if ($opts{restart}) {
|
180
|
+
restart_server(%opts);
|
181
|
+
exit 0;
|
182
|
+
}
|
183
|
+
|
184
|
+
# validate options
|
185
|
+
die "server program not specified\n"
|
186
|
+
unless @ARGV;
|
187
|
+
|
188
|
+
start_server(
|
189
|
+
%opts,
|
190
|
+
exec => \@ARGV,
|
191
|
+
);
|
192
|
+
|
193
|
+
__END__
|
194
|
+
|
195
|
+
=head1 NAME
|
196
|
+
|
197
|
+
start_server - a superdaemon for hot-deploying server programs
|
198
|
+
|
199
|
+
=head1 SYNOPSIS
|
200
|
+
|
201
|
+
start_server [options] -- server-prog server-arg1 server-arg2 ...
|
202
|
+
|
203
|
+
# start Plack using Starlet listening at TCP port 8000
|
204
|
+
start_server --port=8000 -- plackup -s Starlet --max-workers=100 index.psgi
|
205
|
+
|
206
|
+
=head1 DESCRIPTION
|
207
|
+
|
208
|
+
This script is a frontend of L<Server::Starter>. For more information please refer to the documentation of the module.
|
209
|
+
|
210
|
+
=head1 OPTIONS
|
211
|
+
|
212
|
+
=head2 --port=(port|host:port)
|
213
|
+
|
214
|
+
TCP port to listen to (if omitted, will not bind to any ports)
|
215
|
+
|
216
|
+
=head2 --path=path
|
217
|
+
|
218
|
+
path at where to listen using unix socket (optional)
|
219
|
+
|
220
|
+
=head2 --dir=path
|
221
|
+
|
222
|
+
working directory, start_server do chdir to before exec (optional)
|
223
|
+
|
224
|
+
=head2 --interval=seconds
|
225
|
+
|
226
|
+
minimum interval to respawn the server program (default: 1)
|
227
|
+
|
228
|
+
=head2 --signal-on-hup=SIGNAL
|
229
|
+
|
230
|
+
name of the signal to be sent to the server process when start_server receives a SIGHUP (default: SIGTERM). If you use this option, be sure to also use C<--signal-on-term> below.
|
231
|
+
|
232
|
+
=head2 --signal-on-term=SIGNAL
|
233
|
+
|
234
|
+
name of the signal to be sent to the server process when start_server receives a SIGTERM (default: SIGTERM)
|
235
|
+
|
236
|
+
=head2 --pid-file=filename
|
237
|
+
|
238
|
+
if set, writes the process id of the start_server process to the file
|
239
|
+
|
240
|
+
=head2 --status-file=filename
|
241
|
+
|
242
|
+
if set, writes the status of the server process(es) to the file
|
243
|
+
|
244
|
+
=head2 --envdir=ENVDIR
|
245
|
+
|
246
|
+
directory that contains environment variables to the server processes.
|
247
|
+
It is intended for use with C<envdir> in C<daemontools>.
|
248
|
+
This can be overwritten by environment variable C<ENVDIR>.
|
249
|
+
|
250
|
+
=head2 --enable-auto-restart
|
251
|
+
|
252
|
+
enables automatic restart by time.
|
253
|
+
This can be overwritten by environment variable C<ENABLE_AUTO_RESTART>.
|
254
|
+
|
255
|
+
=head2 --auto-restart-interval=seconds
|
256
|
+
|
257
|
+
automatic restart interval (default 360). It is used with C<--enable-auto-restart> option.
|
258
|
+
This can be overwritten by environment variable C<AUTO_RESTART_INTERVAL>.
|
259
|
+
|
260
|
+
=head2 --kill-old-delay=seconds
|
261
|
+
|
262
|
+
time to suspend to send a signal to the old worker. The default value is 5 when C<--enable-auto-restart> is set, 0 otherwise.
|
263
|
+
This can be overwritten by environment variable C<KILL_OLD_DELAY>.
|
264
|
+
|
265
|
+
=head2 --restart
|
266
|
+
|
267
|
+
this is a wrapper command that reads the pid of the start_server process from --pid-file, sends SIGHUP to the process and waits until the server(s) of the older generation(s) die by monitoring the contents of the --status-file
|
268
|
+
|
269
|
+
=head2 --backlog
|
270
|
+
specifies a listen backlog parameter, whose default is SOMAXCONN (usually 128 on Linux). While SOMAXCONN is enough for most loads, large backlog is required for heavy loads.
|
271
|
+
|
272
|
+
=head2 --help
|
273
|
+
|
274
|
+
prints this help
|
275
|
+
|
276
|
+
=head2 --version
|
277
|
+
|
278
|
+
prints the version number
|
279
|
+
|
280
|
+
=head1 AUTHOR
|
281
|
+
|
282
|
+
Kazuho Oku
|
283
|
+
|
284
|
+
=head1 SEE ALSO
|
285
|
+
|
286
|
+
L<Server::Starter>
|
287
|
+
|
288
|
+
=head1 LICENSE
|
289
|
+
|
290
|
+
This library is free software; you can redistribute it and/or modify it under the same terms as Perl itself.
|
291
|
+
|
292
|
+
=cut
|
data/example/Gemfile
ADDED
@@ -0,0 +1,46 @@
|
|
1
|
+
worker_processes 2
|
2
|
+
preload_app true
|
3
|
+
|
4
|
+
APP_ROOT = File.expand_path('../..', __FILE__)
|
5
|
+
status_file = File.join(APP_ROOT, 'log/unicorn.stat')
|
6
|
+
|
7
|
+
if ENV.key?('SERVER_STARTER_PORT')
|
8
|
+
fds = ENV['SERVER_STARTER_PORT'].split(';').map { |x|
|
9
|
+
path_or_port, fd = x.split('=', 2)
|
10
|
+
fd
|
11
|
+
}
|
12
|
+
ENV['UNICORN_FD'] = fds.join(',')
|
13
|
+
ENV.delete('SERVER_STARTER_PORT')
|
14
|
+
else
|
15
|
+
listen ENV['PORT'] || '10080'
|
16
|
+
end
|
17
|
+
|
18
|
+
before_fork do |server, worker|
|
19
|
+
defined?(ActiveRecord::Base) and ActiveRecord::Base.connection.disconnect!
|
20
|
+
|
21
|
+
# Throttle the master from forking too quickly by sleeping. Due
|
22
|
+
# to the implementation of standard Unix signal handlers, this
|
23
|
+
# helps (but does not completely) prevent identical, repeated signals
|
24
|
+
# from being lost when the receiving process is busy.
|
25
|
+
sleep 1
|
26
|
+
end
|
27
|
+
|
28
|
+
after_fork do |server, worker|
|
29
|
+
defined?(ActiveRecord::Base) and ActiveRecord::Base.establish_connection
|
30
|
+
|
31
|
+
begin
|
32
|
+
# This allows a new master process to incrementally
|
33
|
+
# phase out the old master process with SIGTTOU to avoid a
|
34
|
+
# thundering herd (especially in the "preload_app false" case)
|
35
|
+
# when doing a transparent upgrade. The last worker spawned
|
36
|
+
# will then kill off the old master process with a SIGQUIT.
|
37
|
+
pids = File.readlines(status_file).map {|_| _.chomp.split(':') }.to_h
|
38
|
+
old_gen = ENV['SERVER_STARTER_GENERATION'].to_i - 1
|
39
|
+
if old_pid = pids[old_gen.to_s]
|
40
|
+
sig = (worker.nr + 1) >= server.worker_processes ? :QUIT : :TTOU
|
41
|
+
Process.kill(sig, old_pid.to_i)
|
42
|
+
end
|
43
|
+
rescue Errno::ENOENT, Errno::ESRCH => e
|
44
|
+
$stderr.puts "#{e.class} #{e.message}"
|
45
|
+
end
|
46
|
+
end
|
data/example/config.ru
ADDED
data/example/env/TEST
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
foo
|
File without changes
|
@@ -0,0 +1,16 @@
|
|
1
|
+
#!/bin/sh
|
2
|
+
bundle exec start_server.rb \
|
3
|
+
--port=0.0.0.0:10080 \
|
4
|
+
--dir=$(pwd) \
|
5
|
+
--interval=1 \
|
6
|
+
--signal-on-hup=CONT \
|
7
|
+
--signal-on-TERM=TERM \
|
8
|
+
--pid-file=$(pwd)/log/start_server.pid \
|
9
|
+
--status-file=$(pwd)/log/unicorn.stat \
|
10
|
+
--envdir=env \
|
11
|
+
--enable-auto-restart \
|
12
|
+
--auto-restart-interval=100 \
|
13
|
+
--kill-old-delay=1 \
|
14
|
+
--backlog=100 \
|
15
|
+
-- \
|
16
|
+
bundle exec --keep-file-descriptors unicorn -c config/unicorn.conf config.ru 2>&1
|
@@ -0,0 +1,52 @@
|
|
1
|
+
class Server
|
2
|
+
class Starter
|
3
|
+
module Helper
|
4
|
+
def warn(msg)
|
5
|
+
$stderr.puts msg
|
6
|
+
end
|
7
|
+
|
8
|
+
def croak(msg)
|
9
|
+
$stderr.puts msg
|
10
|
+
exit 1
|
11
|
+
end
|
12
|
+
|
13
|
+
def die(*args)
|
14
|
+
if args.size == 2
|
15
|
+
e, msg = args[0], args[1]
|
16
|
+
$stderr.puts "#{msg}:#{e.class} #{e.message}"
|
17
|
+
else
|
18
|
+
msg = args[0]
|
19
|
+
$stderr.puts msg
|
20
|
+
end
|
21
|
+
exit 1
|
22
|
+
end
|
23
|
+
|
24
|
+
def with_local_env(local_env, &block)
|
25
|
+
orig_env = local_env.keys.map {|k| [k, ENV[k]] }.to_h
|
26
|
+
ENV.update(local_env)
|
27
|
+
yield
|
28
|
+
ensure
|
29
|
+
ENV.update(orig_env)
|
30
|
+
end
|
31
|
+
|
32
|
+
def bundler_with_clean_env(&block)
|
33
|
+
if defined?(Bundler)
|
34
|
+
begin
|
35
|
+
# Bundler.with_clean_env resets ENV to initial env on loading ruby
|
36
|
+
orig_env = ENV.to_hash
|
37
|
+
ENV.delete_if { |k,_| k[0,7] == 'BUNDLE_' }
|
38
|
+
if ENV.has_key? 'RUBYOPT'
|
39
|
+
ENV['RUBYOPT'] = ENV['RUBYOPT'].sub '-rbundler/setup', ''
|
40
|
+
end
|
41
|
+
%w[RUBYLIB GEM_HOME].each {|key| ENV.delete(key) }
|
42
|
+
yield
|
43
|
+
ensure
|
44
|
+
ENV.replace(orig_env)
|
45
|
+
end
|
46
|
+
else
|
47
|
+
yield
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|