mirror of
https://github.com/rails/rails.git
synced 2022-11-09 12:12:34 -05:00
cc0105cef9
git-svn-id: http://svn-commit.rubyonrails.org/rails/trunk@1995 5ecf4fe2-1ee6-0310-87b1-e25e094e27de
131 lines
4.3 KiB
Ruby
Executable file
131 lines
4.3 KiB
Ruby
Executable file
#!/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 -wwax") || line.include?("grep")
|
|
pid, *command = line.split
|
|
new(pid, command.join(" "))
|
|
}.compact
|
|
end
|
|
|
|
private
|
|
def process_lines_with_keyword(keyword)
|
|
`ps -wwax -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 usr1
|
|
`kill -s USR1 #{@pid}`
|
|
end
|
|
|
|
def to_s
|
|
"[#{@pid}] #{@command}"
|
|
end
|
|
end
|
|
|
|
OPTIONS = {
|
|
:action => "graceful",
|
|
:dispatcher => File.expand_path(File.dirname(__FILE__) + '/../../public/dispatch.fcgi'),
|
|
:spinner => File.expand_path(File.dirname(__FILE__) + '/spinner'),
|
|
:toggle_spin => true,
|
|
: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.
|
|
|
|
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("-s", "--spinner=path", "default: #{OPTIONS[:spinner]}", String) { |OPTIONS[:spinner]| }
|
|
opts.on("-t", "--toggle-spin", "Whether to send a USR1 to the spinner before and after the reaping (default: true)") { |OPTIONS[:toggle_spin]| }
|
|
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("usr1", OPTIONS[:spinner]) if OPTIONS[:toggle_spin]
|
|
ProgramProcess.process_keywords(OPTIONS[:action], OPTIONS[:dispatcher])
|
|
nudge(OPTIONS[:nudge], OPTIONS[:iterations]) if OPTIONS[:nudge]
|
|
ProgramProcess.process_keywords("usr1", OPTIONS[:spinner]) if OPTIONS[:toggle_spin]
|