thin-preforker 0.0.2 → 0.0.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/Gemfile.lock +1 -1
- data/lib/thin/preforker/callbacks.rb +31 -0
- data/lib/thin/preforker/controller.rb +106 -40
- data/lib/thin/preforker/runner.rb +43 -1
- data/lib/thin/preforker/version.rb +12 -10
- data/lib/thin/preforker.rb +3 -3
- data/sample/callbacks.rb +7 -0
- metadata +6 -4
data/Gemfile.lock
CHANGED
@@ -0,0 +1,31 @@
|
|
1
|
+
module Thin
|
2
|
+
module Preforker
|
3
|
+
class Callbacks
|
4
|
+
attr_accessor :before_fork_callbacks, :after_fork_callbacks
|
5
|
+
|
6
|
+
def initialize filename = nil
|
7
|
+
@before_fork_callbacks = []
|
8
|
+
@after_fork_callbacks = []
|
9
|
+
|
10
|
+
instance_eval(open(filename).read, filename) if filename
|
11
|
+
end
|
12
|
+
|
13
|
+
def run_before_fork_callbacks *args
|
14
|
+
@before_fork_callbacks.each { |callback| callback.call(*args) }
|
15
|
+
end
|
16
|
+
|
17
|
+
def run_after_fork_callbacks *args
|
18
|
+
@after_fork_callbacks.each { |callback| callback.call(*args) }
|
19
|
+
end
|
20
|
+
|
21
|
+
private
|
22
|
+
def before_fork &block
|
23
|
+
@before_fork_callbacks << block
|
24
|
+
end
|
25
|
+
|
26
|
+
def after_fork &block
|
27
|
+
@after_fork_callbacks << block
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -1,60 +1,59 @@
|
|
1
1
|
module Thin
|
2
2
|
module Preforker
|
3
3
|
class Controller < Thin::Controllers::Cluster
|
4
|
-
def initialize
|
5
|
-
|
4
|
+
def initialize options
|
5
|
+
@options = options
|
6
6
|
|
7
|
-
|
7
|
+
if @options[:socket]
|
8
|
+
@options.delete(:address)
|
9
|
+
@options.delete(:port)
|
10
|
+
end
|
11
|
+
|
12
|
+
GC.copy_on_write_friendly = true if GC.respond_to?(:copy_on_write_friendly=)
|
8
13
|
end
|
9
14
|
|
10
15
|
# Start the servers
|
11
16
|
def start
|
17
|
+
daemonize_prefoker if @options[:daemonize]
|
18
|
+
|
12
19
|
with_each_server do |n|
|
13
20
|
start_server n, app
|
14
|
-
sleep 0.1 # Let the OS breath
|
15
21
|
end
|
22
|
+
|
23
|
+
Process.waitall
|
16
24
|
end
|
17
25
|
|
18
26
|
# Start a single server
|
19
27
|
def start_server(number, app)
|
20
28
|
log "Starting server on #{server_id(number)} ... "
|
21
29
|
|
22
|
-
|
23
|
-
|
30
|
+
server = Thin::Server.new(server_options_for(number)[:socket] || server_options_for(number)[:address],
|
31
|
+
server_options_for(number)[:port],
|
32
|
+
server_options_for(number))
|
33
|
+
|
34
|
+
# Set options
|
35
|
+
server.pid_file = server_options_for(number)[:pid]
|
36
|
+
server.log_file = server_options_for(number)[:log]
|
37
|
+
server.timeout = server_options_for(number)[:timeout]
|
38
|
+
server.maximum_connections = server_options_for(number)[:max_conns]
|
39
|
+
server.maximum_persistent_connections = server_options_for(number)[:max_persistent_conns]
|
40
|
+
server.threaded = server_options_for(number)[:threaded]
|
41
|
+
server.no_epoll = server_options_for(number)[:no_epoll] if server.backend.respond_to?(:no_epoll=)
|
42
|
+
|
43
|
+
# ssl support
|
44
|
+
if server_options_for(number)[:ssl]
|
45
|
+
server.ssl = true
|
46
|
+
server.ssl_options = { :private_key_file => server_options_for(number)[:ssl_key_file], :cert_chain_file => server_options_for(number)[:ssl_cert_file], :verify_peer => server_options_for(number)[:ssl_verify] }
|
47
|
+
end
|
24
48
|
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
$stdout.reopen($stdout)
|
30
|
-
$stderr.reopen($stderr)
|
31
|
-
$stdin.reopen("/dev/null")
|
49
|
+
before_fork_for(server, number)
|
50
|
+
|
51
|
+
Process.fork do
|
52
|
+
after_fork_for(server, number)
|
32
53
|
|
33
54
|
# Constantize backend class
|
34
55
|
server_options_for(number)[:backend] = eval(server_options_for(number)[:backend], TOPLEVEL_BINDING) if server_options_for(number)[:backend]
|
35
56
|
|
36
|
-
server = Thin::Server.new(server_options_for(number)[:socket] || server_options_for(number)[:address], # Server detects kind of socket
|
37
|
-
server_options_for(number)[:port], # Port ignored on UNIX socket
|
38
|
-
server_options_for(number))
|
39
|
-
|
40
|
-
# Set options
|
41
|
-
server.pid_file = server_options_for(number)[:pid]
|
42
|
-
server.log_file = server_options_for(number)[:log]
|
43
|
-
server.timeout = server_options_for(number)[:timeout]
|
44
|
-
server.maximum_connections = server_options_for(number)[:max_conns]
|
45
|
-
server.maximum_persistent_connections = server_options_for(number)[:max_persistent_conns]
|
46
|
-
server.threaded = server_options_for(number)[:threaded]
|
47
|
-
server.no_epoll = server_options_for(number)[:no_epoll] if server.backend.respond_to?(:no_epoll=)
|
48
|
-
|
49
|
-
# ssl support
|
50
|
-
if server_options_for(number)[:ssl]
|
51
|
-
server.ssl = true
|
52
|
-
server.ssl_options = { :private_key_file => server_options_for(number)[:ssl_key_file], :cert_chain_file => server_options_for(number)[:ssl_cert_file], :verify_peer => server_options_for(number)[:ssl_verify] }
|
53
|
-
end
|
54
|
-
|
55
|
-
# Detach the process, after this line the current process returns
|
56
|
-
server.daemonize if server_options_for(number)[:daemonize]
|
57
|
-
|
58
57
|
# +config+ must be called before changing privileges since it might require superuser power.
|
59
58
|
server.config
|
60
59
|
|
@@ -68,11 +67,11 @@ module Thin
|
|
68
67
|
|
69
68
|
# If a stats URL is specified, wrap in Stats adapter
|
70
69
|
server.app = Thin::Stats::Adapter.new(server.app, server_options_for(number)[:stats]) if server_options_for(number)[:stats]
|
71
|
-
|
72
|
-
reopen_logs logs
|
73
70
|
|
74
71
|
server.start
|
75
72
|
end
|
73
|
+
|
74
|
+
wait_for_file :creation, server_options_for(number)[:pid]
|
76
75
|
end
|
77
76
|
|
78
77
|
# Stop the servers
|
@@ -97,13 +96,15 @@ module Thin
|
|
97
96
|
|
98
97
|
# Stop and start the servers.
|
99
98
|
def restart
|
100
|
-
|
99
|
+
daemonize_prefoker if @options[:daemonize]
|
101
100
|
|
102
101
|
with_each_server do |n|
|
103
102
|
stop_server n
|
104
103
|
start_server n, app
|
105
104
|
sleep 0.1 # Let the OS breath
|
106
105
|
end
|
106
|
+
|
107
|
+
Process.waitall
|
107
108
|
end
|
108
109
|
|
109
110
|
private
|
@@ -112,6 +113,10 @@ module Thin
|
|
112
113
|
# a Rack adapter from it. Or else we guess which adapter to use and load it.
|
113
114
|
@app ||= @options[:rackup] ? load_rackup_config : load_adapter
|
114
115
|
end
|
116
|
+
|
117
|
+
def callbacks
|
118
|
+
@callbacks ||= Callbacks.new @options[:callbacks]
|
119
|
+
end
|
115
120
|
|
116
121
|
def server_options_for(number)
|
117
122
|
@server_options ||= {}
|
@@ -119,7 +124,7 @@ module Thin
|
|
119
124
|
|
120
125
|
# Sets the server options for this server
|
121
126
|
@server_options[number] = @options.reject { |option, value| CLUSTER_OPTIONS.include?(option) }
|
122
|
-
@server_options[number].merge!(:pid => pid_file_for(number), :log => log_file_for(number))
|
127
|
+
@server_options[number].merge!(:pid => pid_file_for(number), :log => log_file_for(number), :daemonize => nil)
|
123
128
|
if socket
|
124
129
|
@server_options[number].merge!(:socket => socket_for(number))
|
125
130
|
elsif swiftiply?
|
@@ -135,7 +140,7 @@ module Thin
|
|
135
140
|
require 'fcntl'
|
136
141
|
|
137
142
|
logs = []
|
138
|
-
|
143
|
+
|
139
144
|
|
140
145
|
logs
|
141
146
|
end
|
@@ -143,6 +148,67 @@ module Thin
|
|
143
148
|
def reopen_logs logs
|
144
149
|
ObjectSpace.each_object(File) { |fp| fp.reopen(fp.path, "a") if logs.include? fp.path }
|
145
150
|
end
|
151
|
+
|
152
|
+
def before_fork_for(server, number)
|
153
|
+
raise ArgumentError, "You must specify a pid file to fork" unless server_options_for(number)[:pid]
|
154
|
+
raise ArgumentError, "You must specify a log file to fork" unless server_options_for(number)[:log]
|
155
|
+
|
156
|
+
server.send(:remove_stale_pid_file)
|
157
|
+
|
158
|
+
@pwd = Dir.pwd # Current directory is changed during fork, so store it
|
159
|
+
|
160
|
+
FileUtils.mkdir_p File.dirname(server_options_for(number)[:pid])
|
161
|
+
FileUtils.mkdir_p File.dirname(server_options_for(number)[:log])
|
162
|
+
|
163
|
+
@logs = []
|
164
|
+
ObjectSpace.each_object(File) { |fp| @logs << fp.path if fp.fcntl(Fcntl::F_GETFL) == File::APPEND | File::WRONLY rescue false }
|
165
|
+
|
166
|
+
callbacks.run_before_fork_callbacks server, number
|
167
|
+
end
|
168
|
+
|
169
|
+
def after_fork_for(server, number)
|
170
|
+
log_fp = open(server_options_for(number)[:log], "a")
|
171
|
+
log_fp.sync = true
|
172
|
+
$stdout.reopen(log_fp)
|
173
|
+
$stderr.reopen(log_fp)
|
174
|
+
$stdin.reopen("/dev/null")
|
175
|
+
|
176
|
+
Dir.chdir(@pwd)
|
177
|
+
|
178
|
+
server.send(:write_pid_file)
|
179
|
+
server.send(:at_exit) do
|
180
|
+
log ">> Exiting!"
|
181
|
+
server.send(:remove_pid_file)
|
182
|
+
end
|
183
|
+
|
184
|
+
Signal.trap("INT") { server.stop! }
|
185
|
+
Signal.trap("TERM") { server.stop }
|
186
|
+
Signal.trap("QUIT") { server.stop } unless Thin.win?
|
187
|
+
|
188
|
+
ObjectSpace.each_object(File) { |fp| fp.reopen(fp.path, "a") if @logs.include? fp.path }
|
189
|
+
|
190
|
+
callbacks.run_after_fork_callbacks server, number
|
191
|
+
end
|
192
|
+
|
193
|
+
def daemonize_prefoker
|
194
|
+
raise ArgumentError, "You must specify a preforker pid file to daemonize" unless @options[:preforker_pid]
|
195
|
+
raise ArgumentError, "You must specify a preforker log file to daemonize" unless @options[:preforker_log]
|
196
|
+
|
197
|
+
pwd = Dir.pwd # Current directory is changed during fork, so store it
|
198
|
+
|
199
|
+
FileUtils.mkdir_p File.dirname(@options[:preforker_pid])
|
200
|
+
FileUtils.mkdir_p File.dirname(@options[:preforker_log])
|
201
|
+
|
202
|
+
Daemonize.daemonize
|
203
|
+
|
204
|
+
Dir.chdir(pwd)
|
205
|
+
|
206
|
+
Daemonize.redirect_io @options[:preforker_log]
|
207
|
+
|
208
|
+
log ">> Writing PID to #{@options[:preforker_pid]}"
|
209
|
+
open(@options[:preforker_pid],"w") { |f| f.write(Process.pid) }
|
210
|
+
File.chmod(0644, @options[:preforker_pid])
|
211
|
+
end
|
146
212
|
end
|
147
213
|
end
|
148
214
|
end
|
@@ -2,9 +2,51 @@ class Thin::Preforker::Runner < Thin::Runner
|
|
2
2
|
# Error raised that will abort the process and print not backtrace.
|
3
3
|
class RunnerError < RuntimeError; end
|
4
4
|
|
5
|
+
def initialize argv
|
6
|
+
@argv = argv
|
7
|
+
|
8
|
+
# Default options values
|
9
|
+
@options = {
|
10
|
+
:chdir => Dir.pwd,
|
11
|
+
:environment => ENV['RACK_ENV'] || 'development',
|
12
|
+
:address => '0.0.0.0',
|
13
|
+
:port => Thin::Server::DEFAULT_PORT,
|
14
|
+
:timeout => Thin::Server::DEFAULT_TIMEOUT,
|
15
|
+
:log => 'log/thin.log',
|
16
|
+
:pid => 'tmp/pids/thin.pid',
|
17
|
+
:max_conns => Thin::Server::DEFAULT_MAXIMUM_CONNECTIONS,
|
18
|
+
:max_persistent_conns => Thin::Server::DEFAULT_MAXIMUM_PERSISTENT_CONNECTIONS,
|
19
|
+
:require => [],
|
20
|
+
:wait => Thin::Preforker::Controller::DEFAULT_WAIT_TIME,
|
21
|
+
:daemonize => true,
|
22
|
+
:preforker_callbacks => nil,
|
23
|
+
:preforker_log => "log/thin-preforker.log",
|
24
|
+
:preforker_pid => "tmp/pids/thin-preforker.pid"
|
25
|
+
}
|
26
|
+
|
27
|
+
parse!
|
28
|
+
end
|
29
|
+
|
30
|
+
def parser
|
31
|
+
super
|
32
|
+
|
33
|
+
@parser.banner = "Usage: #{@parser.program_name} [options] #{self.class.commands.join('|')}"
|
34
|
+
|
35
|
+
@parser.tap do |opts|
|
36
|
+
# TODO change to #on instead of #on_tail after thin is fixed.
|
37
|
+
opts.on_tail ""
|
38
|
+
opts.on_tail "Preforker options:"
|
39
|
+
|
40
|
+
opts.on_tail("--callbacks FILE", "Path to preforker callbacks file") { |file| @options[:callbacks] = file }
|
41
|
+
opts.on_tail("--preforker-log FILE", "File to redirect preforker output " + "(default: #{@options[:preforker_log]})") { |file| @options[:preforker_log] = file }
|
42
|
+
opts.on_tail("--preforker-pid FILE", "File to store preforker PID " + "(default: #{@options[:preforker_pid]})") { |file| @options[:preforker_pid] = file }
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
|
5
47
|
def run_command
|
6
48
|
load_options_from_config_file! unless CONFIGLESS_COMMANDS.include?(@command)
|
7
|
-
|
49
|
+
|
8
50
|
# PROGRAM_NAME is relative to the current directory, so make sure
|
9
51
|
# we store and expand it before changing directory.
|
10
52
|
Thin::Command.script = File.expand_path($PROGRAM_NAME)
|
@@ -1,14 +1,16 @@
|
|
1
|
-
|
2
|
-
module
|
3
|
-
module
|
4
|
-
|
5
|
-
|
6
|
-
|
1
|
+
unless defined?(Thin::Preforker::VERSION)
|
2
|
+
module Thin
|
3
|
+
module Preforker
|
4
|
+
module VERSION #:nodoc:
|
5
|
+
MAJOR = 0
|
6
|
+
MINOR = 0
|
7
|
+
TINY = 3
|
7
8
|
|
8
|
-
|
9
|
-
|
9
|
+
STRING = [MAJOR, MINOR, TINY].join('.')
|
10
|
+
end
|
10
11
|
|
11
|
-
|
12
|
-
|
12
|
+
NAME = 'thin-preforker'.freeze
|
13
|
+
SERVER = "#{NAME} #{VERSION::STRING}".freeze
|
14
|
+
end
|
13
15
|
end
|
14
16
|
end
|
data/lib/thin/preforker.rb
CHANGED
@@ -2,9 +2,9 @@ require "thin"
|
|
2
2
|
|
3
3
|
module Thin
|
4
4
|
module Preforker
|
5
|
+
autoload :Callbacks, "thin/preforker/callbacks"
|
5
6
|
autoload :Controller, "thin/preforker/controller"
|
6
7
|
autoload :Runner, "thin/preforker/runner"
|
8
|
+
autoload :Version, "thin/preforker/version"
|
7
9
|
end
|
8
|
-
end
|
9
|
-
|
10
|
-
require "thin/preforker/version"
|
10
|
+
end
|
data/sample/callbacks.rb
ADDED
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: thin-preforker
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.3
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2012-07-
|
12
|
+
date: 2012-07-11 00:00:00.000000000 Z
|
13
13
|
dependencies: []
|
14
14
|
description: A thin controller and spawner with prefork
|
15
15
|
email: rafael.gaspar@me.com
|
@@ -26,9 +26,11 @@ files:
|
|
26
26
|
- Rakefile
|
27
27
|
- bin/thin-preforker
|
28
28
|
- lib/thin/preforker.rb
|
29
|
+
- lib/thin/preforker/callbacks.rb
|
29
30
|
- lib/thin/preforker/controller.rb
|
30
31
|
- lib/thin/preforker/runner.rb
|
31
32
|
- lib/thin/preforker/version.rb
|
33
|
+
- sample/callbacks.rb
|
32
34
|
- thin-preforker.gemspec
|
33
35
|
homepage: http://github.com/rafaelgaspar/thin-preforker/
|
34
36
|
licenses:
|
@@ -45,7 +47,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
45
47
|
version: '0'
|
46
48
|
segments:
|
47
49
|
- 0
|
48
|
-
hash:
|
50
|
+
hash: 1366569596521026037
|
49
51
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
50
52
|
none: false
|
51
53
|
requirements:
|
@@ -54,7 +56,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
54
56
|
version: '0'
|
55
57
|
segments:
|
56
58
|
- 0
|
57
|
-
hash:
|
59
|
+
hash: 1366569596521026037
|
58
60
|
requirements: []
|
59
61
|
rubyforge_project:
|
60
62
|
rubygems_version: 1.8.10
|