Added convenience controls for FCGI processes (especially when managed...

Added convenience controls for FCGI processes (especially when managed remotely): spinner, spawner, and reaper. They reside in script/process. More details can be had by calling them with -h/--help

git-svn-id: http://svn-commit.rubyonrails.org/rails/trunk@1909 5ecf4fe2-1ee6-0310-87b1-e25e094e27de
上级 eddd7c45
*SVN*
* Added convenience controls for FCGI processes (especially when managed remotely): spinner, spawner, and reaper. They reside in script/process. More details can be had by calling them with -h/--help.
* Added load_fixtures task to the Rakefile, which will load all the fixtures into the database for the current environment #1791 [Marcel Molina]
* Added an empty robots.txt to public/, so that web servers asking for it won't trigger a dynamic call, like favicon.ico #1738 [michael@schubert]
......
......@@ -26,14 +26,14 @@ RUBY_FORGE_USER = "webster132"
# end
BASE_DIRS = %w( app config/environments components db doc log lib public script test vendor )
BASE_DIRS = %w( app config/environments components db doc log lib public script script/process test vendor )
APP_DIRS = %w( apis models controllers helpers views views/layouts )
PUBLIC_DIRS = %w( images javascripts stylesheets )
TEST_DIRS = %w( fixtures unit functional mocks mocks/development mocks/test )
LOG_FILES = %w( server.log development.log test.log production.log )
HTML_FILES = %w( 404.html 500.html index.html robots.txt favicon.ico javascripts/prototype.js javascripts/effects.js javascripts/dragdrop.js javascripts/controls.js )
BIN_FILES = %w( generate destroy breakpointer console server update runner profiler benchmarker ) # listener tracker
BIN_FILES = %w( generate destroy breakpointer console server update runner profiler benchmarker process/reaper process/spinner process/spawner )
VENDOR_LIBS = %w( actionpack activerecord actionmailer activesupport actionwebservice railties )
......
#!/usr/local/bin/ruby
require 'optparse'
require 'net/http'
require 'uri'
def nudge(url, iterations)
print "Nudging #{url}: "
iterations.times { Net::HTTP.get_response(URI.parse(url)); print "."; STDOUT.flush }
puts
end
if RUBY_PLATFORM =~ /mswin32/ then abort("Reaper is only for Unix") end
class ProgramProcess
class << self
def process_keywords(action, *keywords)
processes = keywords.collect { |keyword| find_by_keyword(keyword) }.flatten
if processes.empty?
puts "Couldn't find any process matching: #{keywords.join(" or ")}"
else
processes.each do |process|
puts "#{action.capitalize}ing #{process}"
process.send(action)
end
end
end
def find_by_keyword(keyword)
process_lines_with_keyword(keyword).split("\n").collect { |line|
next if line.include?("inq") || line.include?("ps -ax") || line.include?("grep")
pid, *command = line.split
new(pid, command.join(" "))
}.compact
end
private
def process_lines_with_keyword(keyword)
`ps -ax -o 'pid command' | grep #{keyword}`
end
end
def initialize(pid, command)
@pid, @command = pid, command
end
def find
end
def reload
`kill -s HUP #{@pid}`
end
def graceful
`kill -s TERM #{@pid}`
end
def kill
`kill -9 #{@pid}`
end
def to_s
"[#{@pid}] #{@command}"
end
end
OPTIONS = {
:action => "graceful",
:dispatcher => File.expand_path(File.dirname(__FILE__) + '/../../public/dispatch.fcgi'),
:iterations => 10,
:nudge => false
}
ARGV.options do |opts|
opts.banner = "Usage: reaper [options]"
opts.separator ""
opts.on <<-EOF
Description:
The reaper is used to reload, gracefully exit, and forcefully exit FCGI processes
running a Rails Dispatcher. This is commonly done when a new version of the application
is available, so the existing processes can be updated to use the latest code.
The reaper actions are:
* reload : Only reloads the application, but not the framework (like the development environment)
* graceful: Marks all of the processes for exit after the next request
* kill : Forcefully exists all processes regardless of whether they're currently serving a request
Graceful exist is the most common and default action. But since the processes won't exist until after
their next request, it's often necessary to ensure that such a request occurs right after they've been
marked. That's what nudging is for.
A nudge is simply a request to a URL where the dispatcher is serving. You should perform one nudge per
FCGI process you have running if they're setup in a round-robin. Be sure to do one nudge per FCGI process
across all your servers. So three servers with 10 processes each should nudge 30 times to be sure all processes
are restarted.
NOTE: You're responsible for restarting the processes after they exit. This can be automated by using
the spinner.
Examples:
reaper -a reload
reaper -n http://www.example.com -i 10 # gracefully exit, nudge 10 times
EOF
opts.on(" Options:")
opts.on("-a", "--action=name", "reload|graceful|kill (default: #{OPTIONS[:action]})", String) { |OPTIONS[:action]| }
opts.on("-d", "--dispatcher=path", "default: #{OPTIONS[:dispatcher]}", String) { |OPTIONS[:dispatcher]| }
opts.on("-n", "--nudge=url", "Should point to URL that's handled by the FCGI process", String) { |OPTIONS[:nudge]| }
opts.on("-i", "--iterations=number", "One nudge per FCGI process running (default: #{OPTIONS[:iterations]})", Integer) { |OPTIONS[:iterations]| }
opts.separator ""
opts.on("-h", "--help", "Show this help message.") { puts opts; exit }
opts.parse!
end
ProgramProcess.process_keywords(OPTIONS[:action], OPTIONS[:dispatcher])
nudge(OPTIONS[:nudge], OPTIONS[:iterations]) if OPTIONS[:nudge]
\ No newline at end of file
#!/usr/local/bin/ruby
require 'optparse'
def spawn(port)
print "Starting FCGI on port: #{port}\n "
system("#{OPTIONS[:spawner]} -f #{OPTIONS[:dispatcher]} -p #{port}")
end
OPTIONS = {
:environment => "production",
:spawner => '/usr/bin/env spawn-fcgi',
:dispatcher => File.expand_path(File.dirname(__FILE__) + '/../../public/dispatch.fcgi'),
:port => 8000,
:instances => 3
}
ARGV.options do |opts|
opts.banner = "Usage: spawner [options]"
opts.separator ""
opts.on <<-EOF
Description:
The spawner is a wrapper for spawn-fcgi that makes it easier to start multiple FCGI
processes running the Rails dispatcher. The spawn-fcgi command is included with the lighttpd
web server, but can be used with both Apache and lighttpd (and any other web server supporting
externally managed FCGI processes).
You decide a starting port (default is 8000) and the number of FCGI process instances you'd
like to run. So if you pick 9100 and 3 instances, you'll start processes on 9100, 9101, and 9102.
Examples:
spawner # starts instances on 8000, 8001, and 8002
spawner -p 9100 -i 10 # starts 10 instances counting from 9100 to 9109
EOF
opts.on(" Options:")
opts.on("-p", "--port=number", Integer, "Starting port number (default: #{OPTIONS[:port]})") { |OPTIONS[:port]| }
opts.on("-i", "--instances=number", Integer, "Number of instances (default: #{OPTIONS[:instances]})") { |OPTIONS[:instances]| }
opts.on("-e", "--environment=name", String, "test|development|production (default: #{OPTIONS[:environment]})") { |OPTIONS[:environment]| }
opts.on("-s", "--spawner=path", String, "default: #{OPTIONS[:spawner]}") { |OPTIONS[:spawner]| }
opts.on("-d", "--dispatcher=path", String, "default: #{OPTIONS[:dispatcher]}") { |dispatcher| OPTIONS[:dispatcher] = File.expand_path(dispatcher) }
opts.separator ""
opts.on("-h", "--help", "Show this help message.") { puts opts; exit }
opts.parse!
end
ENV["RAILS_ENV"] = OPTIONS[:environment]
OPTIONS[:instances].times { |i| spawn(OPTIONS[:port] + i) }
\ No newline at end of file
#!/usr/local/bin/ruby
require 'optparse'
def daemonize
exit if fork # Parent exits, child continues.
Process.setsid # Become session leader.
exit if fork # Zap session leader. See [1].
Dir.chdir "/" # Release old working directory.
File.umask 0000 # Ensure sensible umask. Adjust as needed.
STDIN.reopen "/dev/null" # Free file descriptors and
STDOUT.reopen "/dev/null", "a" # point them somewhere sensible.
STDERR.reopen STDOUT # STDOUT/ERR should better go to a logfile.
end
OPTIONS = {
:interval => 1.0,
:command => File.expand_path(File.dirname(__FILE__) + '/spawner'),
:daemon => false
}
ARGV.options do |opts|
opts.banner = "Usage: spinner [options]"
opts.separator ""
opts.on <<-EOF
Description:
The spinner is a protection loop for the spawner, which will attempt to restart any FCGI processes
that might have been restarted or outright crashed. It's a brute-force attempt that'll just try
to run the spawner every X number of seconds, so it does pose a load on the server (~1% on our test
server).
Examples:
spinner # attempts to run the spawner with default settings every second with output on the terminal
spinner -i 3 -d # only run the spawner every 3 seconds and detach from the terminal to become a daemon
spinner -c '/path/to/app/script/process/spawner -p 9000 -i 10' -d # using custom spawner
EOF
opts.on(" Options:")
opts.on("-c", "--command=path", String) { |OPTIONS[:command]| }
opts.on("-i", "--interval=seconds", Float) { |OPTIONS[:interval]| }
opts.on("-d", "--daemon") { |OPTIONS[:daemon]| }
opts.separator ""
opts.on("-h", "--help", "Show this help message.") { puts opts; exit }
opts.parse!
end
daemonize if OPTIONS[:daemon]
loop do
system(OPTIONS[:command])
sleep(OPTIONS[:interval])
end
trap(OPTIONS[:daemon] ? "TERM" : "INT") { exit }
\ No newline at end of file
......@@ -42,8 +42,8 @@ def manifest
m.file "environments/development.rb", "config/environments/development.rb"
m.file "environments/test.rb", "config/environments/test.rb"
# Scripts (tracker listener)
%w(console destroy generate server runner benchmarker profiler ).each do |file|
# Scripts
%w(console destroy generate server runner benchmarker profiler process/reaper process/spinner process/spawner ).each do |file|
m.file "bin/#{file}", "script/#{file}", script_options
end
if options[:gem]
......@@ -56,7 +56,6 @@ def manifest
m.file "dispatches/dispatch.rb", "public/dispatch.rb", script_options
m.file "dispatches/dispatch.rb", "public/dispatch.cgi", script_options
m.file "dispatches/dispatch.fcgi", "public/dispatch.fcgi", script_options
# m.file "dispatches/gateway.cgi", "public/gateway.cgi", script_options
# HTML files
%w(404 500 index).each do |file|
......@@ -116,6 +115,7 @@ def add_options!(opt)
public/javascripts
public/stylesheets
script
script/process
test/fixtures
test/functional
test/mocks/development
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册