diff options
Diffstat (limited to 'railties/bin/process/reaper')
-rwxr-xr-x | railties/bin/process/reaper | 123 |
1 files changed, 123 insertions, 0 deletions
diff --git a/railties/bin/process/reaper b/railties/bin/process/reaper new file mode 100755 index 0000000000..a636d329c8 --- /dev/null +++ b/railties/bin/process/reaper @@ -0,0 +1,123 @@ +#!/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 |