提交 df387ab3 编写于 作者: C Charlie Somerville

remove FastCGI crap

上级 01189596
......@@ -5,7 +5,6 @@ def setup
@env = {
"HTTP_MAX_FORWARDS" => "10",
"SERVER_NAME" => "glu.ttono.us",
"FCGI_ROLE" => "RESPONDER",
"AUTH_TYPE" => "Basic",
"HTTP_X_FORWARDED_HOST" => "glu.ttono.us",
"HTTP_ACCEPT_CHARSET" => "UTF-8",
......
......@@ -159,14 +159,9 @@ end
desc "Make copies of all the default content of ties"
task :copy_ties_content => [
:copy_rootfiles, :copy_dispatches, :copy_html_files, :copy_application,
:copy_rootfiles, :copy_html_files, :copy_application,
:copy_configs, :copy_binfiles, :copy_test_helpers, :copy_app_doc_readme ]
task :copy_dispatches do
copy_with_rewritten_ruby_path("dispatches/dispatch.fcgi", "#{PKG_DESTINATION}/public/dispatch.fcgi")
chmod 0755, "#{PKG_DESTINATION}/public/dispatch.fcgi"
end
task :copy_html_files do
HTML_FILES.each { |file| cp File.join('html', file), File.join(PKG_DESTINATION, 'public', file) }
end
......
#!/usr/bin/env ruby
#
# You may specify the path to the FastCGI crash log (a log of unhandled
# exceptions which forced the FastCGI instance to exit, great for debugging)
# and the number of requests to process before running garbage collection.
#
# By default, the FastCGI crash log is RAILS_ROOT/log/fastcgi.crash.log
# and the GC period is nil (turned off). A reasonable number of requests
# could range from 10-100 depending on the memory footprint of your app.
#
# Example:
# # Default log path, normal GC behavior.
# RailsFCGIHandler.process!
#
# # Default log path, 50 requests between GC.
# RailsFCGIHandler.process! nil, 50
#
# # Custom log path, normal GC behavior.
# RailsFCGIHandler.process! '/var/log/myapp_fcgi_crash.log'
#
require File.dirname(__FILE__) + "/../config/environment"
require 'fcgi_handler'
RailsFCGIHandler.process!
require 'fcgi'
require 'logger'
require 'dispatcher'
require 'rbconfig'
class RailsFCGIHandler
SIGNALS = {
'HUP' => :reload,
'INT' => :exit_now,
'TERM' => :exit_now,
'USR1' => :exit,
'USR2' => :restart
}
GLOBAL_SIGNALS = SIGNALS.keys - %w(USR1)
attr_reader :when_ready
attr_accessor :log_file_path
attr_accessor :gc_request_period
# Initialize and run the FastCGI instance, passing arguments through to new.
def self.process!(*args, &block)
new(*args, &block).process!
end
# Initialize the FastCGI instance with the path to a crash log
# detailing unhandled exceptions (default RAILS_ROOT/log/fastcgi.crash.log)
# and the number of requests to process between garbage collection runs
# (default nil for normal GC behavior.) Optionally, pass a block which
# takes this instance as an argument for further configuration.
def initialize(log_file_path = nil, gc_request_period = nil)
self.log_file_path = log_file_path || "#{RAILS_ROOT}/log/fastcgi.crash.log"
self.gc_request_period = gc_request_period
# Yield for additional configuration.
yield self if block_given?
# Safely install signal handlers.
install_signal_handlers
@app = Dispatcher.new
# Start error timestamp at 11 seconds ago.
@last_error_on = Time.now - 11
end
def process!(provider = FCGI)
mark_features!
dispatcher_log :info, 'starting'
process_each_request provider
dispatcher_log :info, 'stopping gracefully'
rescue Exception => error
case error
when SystemExit
dispatcher_log :info, 'stopping after explicit exit'
when SignalException
dispatcher_error error, 'stopping after unhandled signal'
else
# Retry if exceptions occur more than 10 seconds apart.
if Time.now - @last_error_on > 10
@last_error_on = Time.now
dispatcher_error error, 'retrying after unhandled exception'
retry
else
dispatcher_error error, 'stopping after unhandled exception within 10 seconds of the last'
end
end
end
protected
def process_each_request(provider)
request = nil
catch :exit do
provider.each do |request|
process_request(request)
case when_ready
when :reload
reload!
when :restart
close_connection(request)
restart!
when :exit
close_connection(request)
throw :exit
end
end
end
rescue SignalException => signal
raise unless signal.message == 'SIGUSR1'
close_connection(request)
end
def process_request(request)
@processing, @when_ready = true, nil
gc_countdown
with_signal_handler 'USR1' do
begin
::Rack::Handler::FastCGI.serve(request, @app)
rescue SignalException, SystemExit
raise
rescue Exception => error
dispatcher_error error, 'unhandled dispatch error'
end
end
ensure
@processing = false
end
def logger
@logger ||= Logger.new(@log_file_path)
end
def dispatcher_log(level, msg)
time_str = Time.now.strftime("%d/%b/%Y:%H:%M:%S")
logger.send(level, "[#{time_str} :: #{$$}] #{msg}")
rescue Exception => log_error # Logger errors
STDERR << "Couldn't write to #{@log_file_path.inspect}: #{msg}\n"
STDERR << " #{log_error.class}: #{log_error.message}\n"
end
def dispatcher_error(e, msg = "")
error_message =
"Dispatcher failed to catch: #{e} (#{e.class})\n" +
" #{e.backtrace.join("\n ")}\n#{msg}"
dispatcher_log(:error, error_message)
end
def install_signal_handlers
GLOBAL_SIGNALS.each { |signal| install_signal_handler(signal) }
end
def install_signal_handler(signal, handler = nil)
if SIGNALS.include?(signal) && self.class.method_defined?(name = "#{SIGNALS[signal]}_handler")
handler ||= method(name).to_proc
begin
trap(signal, handler)
rescue ArgumentError
dispatcher_log :warn, "Ignoring unsupported signal #{signal}."
end
else
dispatcher_log :warn, "Ignoring unsupported signal #{signal}."
end
end
def with_signal_handler(signal)
install_signal_handler(signal)
yield
ensure
install_signal_handler(signal, 'DEFAULT')
end
def exit_now_handler(signal)
dispatcher_log :info, "asked to stop immediately"
exit
end
def exit_handler(signal)
dispatcher_log :info, "asked to stop ASAP"
if @processing
@when_ready = :exit
else
throw :exit
end
end
def reload_handler(signal)
dispatcher_log :info, "asked to reload ASAP"
if @processing
@when_ready = :reload
else
reload!
end
end
def restart_handler(signal)
dispatcher_log :info, "asked to restart ASAP"
if @processing
@when_ready = :restart
else
restart!
end
end
def restart!
config = ::Config::CONFIG
ruby = File::join(config['bindir'], config['ruby_install_name']) + config['EXEEXT']
command_line = [ruby, $0, ARGV].flatten.join(' ')
dispatcher_log :info, "restarted"
# close resources as they won't be closed by
# the OS when using exec
logger.close rescue nil
Rails.logger.close rescue nil
exec(command_line)
end
def reload!
run_gc! if gc_request_period
restore!
@when_ready = nil
dispatcher_log :info, "reloaded"
end
# Make a note of $" so we can safely reload this instance.
def mark_features!
@features = $".clone
end
def restore!
$".replace @features
Dispatcher.reset_application!
ActionController::Routing::Routes.reload
end
def run_gc!
@gc_request_countdown = gc_request_period
GC.enable; GC.start; GC.disable
end
def gc_countdown
if gc_request_period
@gc_request_countdown ||= gc_request_period
@gc_request_countdown -= 1
run_gc! if @gc_request_countdown <= 0
end
end
def close_connection(request)
request.finish if request
end
end
......@@ -218,7 +218,6 @@ def create_dispatch_files(m)
dispatcher_options = { :chmod => 0755, :shebang => options[:shebang] }
m.file "dispatches/config.ru", "config.ru"
m.file "dispatches/dispatch.fcgi", "public/dispatch.fcgi", dispatcher_options
end
end
......
......@@ -138,7 +138,6 @@ namespace :rails do
task :generate_dispatchers do
require 'railties_path'
FileUtils.cp(RAILTIES_PATH + '/dispatches/config.ru', RAILS_ROOT + '/config.ru')
FileUtils.cp(RAILTIES_PATH + '/dispatches/dispatch.fcgi', RAILS_ROOT + '/public/dispatch.fcgi')
end
end
end
require 'abstract_unit'
if RUBY_VERSION < '1.9.0'
uses_gem "fcgi", "0.8.7" do
require 'action_controller'
require 'fcgi_handler'
Dispatcher.middleware.clear
class RailsFCGIHandlerTest < Test::Unit::TestCase
def setup
@log = StringIO.new
@handler = RailsFCGIHandler.new(@log)
end
def test_process_restart
request = mock
FCGI.stubs(:each).yields(request)
@handler.expects(:process_request).once
@handler.expects(:dispatcher_error).never
@handler.expects(:when_ready).returns(:restart)
@handler.expects(:close_connection).with(request)
@handler.expects(:reload!).never
@handler.expects(:restart!)
@handler.process!
end
def test_process_exit
request = mock
FCGI.stubs(:each).yields(request)
@handler.expects(:process_request).once
@handler.expects(:dispatcher_error).never
@handler.expects(:when_ready).returns(:exit)
@handler.expects(:close_connection).with(request)
@handler.expects(:reload!).never
@handler.expects(:restart!).never
@handler.process!
end
def test_process_with_system_exit_exception
request = mock
FCGI.stubs(:each).yields(request)
@handler.expects(:process_request).once.raises(SystemExit)
@handler.stubs(:dispatcher_log)
@handler.expects(:dispatcher_log).with(:info, regexp_matches(/^stopping/))
@handler.expects(:dispatcher_error).never
@handler.expects(:when_ready).never
@handler.expects(:close_connection).never
@handler.expects(:reload!).never
@handler.expects(:restart!).never
@handler.process!
end
def test_restart_handler_outside_request
@handler.expects(:dispatcher_log).with(:info, "asked to restart ASAP")
@handler.expects(:restart!).once
@handler.send(:restart_handler, nil)
assert_equal nil, @handler.when_ready
end
def test_install_signal_handler_should_log_on_bad_signal
@handler.stubs(:trap).raises(ArgumentError)
@handler.expects(:dispatcher_log).with(:warn, "Ignoring unsupported signal CHEESECAKE.")
@handler.send(:install_signal_handler, "CHEESECAKE", nil)
end
def test_reload
@handler.expects(:restore!)
@handler.expects(:dispatcher_log).with(:info, "reloaded")
@handler.send(:reload!)
assert_nil @handler.when_ready
end
def test_reload_runs_gc_when_gc_request_period_set
@handler.expects(:run_gc!)
@handler.expects(:restore!)
@handler.expects(:dispatcher_log).with(:info, "reloaded")
@handler.gc_request_period = 10
@handler.send(:reload!)
end
def test_reload_doesnt_run_gc_if_gc_request_period_isnt_set
@handler.expects(:run_gc!).never
@handler.expects(:restore!)
@handler.expects(:dispatcher_log).with(:info, "reloaded")
@handler.send(:reload!)
end
def test_restart!
@handler.expects(:dispatcher_log).with(:info, "restarted")
@handler.expects(:exec).returns('restarted')
assert_equal 'restarted', @handler.send(:restart!)
end
def test_restore!
$".expects(:replace)
Dispatcher.expects(:reset_application!)
ActionController::Routing::Routes.expects(:reload)
@handler.send(:restore!)
end
def test_uninterrupted_processing
request = mock
FCGI.expects(:each).yields(request)
@handler.expects(:process_request).with(request)
@handler.process!
assert_nil @handler.when_ready
end
end
class RailsFCGIHandlerSignalsTest < Test::Unit::TestCase
class ::RailsFCGIHandler
attr_accessor :signal
alias_method :old_gc_countdown, :gc_countdown
def gc_countdown
signal ? Process.kill(signal, $$) : old_gc_countdown
end
end
def setup
@log = StringIO.new
@handler = RailsFCGIHandler.new(@log)
@dispatcher = mock
Dispatcher.stubs(:new).returns(@dispatcher)
end
def test_interrupted_via_HUP_when_not_in_request
request = mock
FCGI.expects(:each).once.yields(request)
@handler.expects(:signal).times(2).returns('HUP')
@handler.expects(:reload!).once
@handler.expects(:close_connection).never
@handler.expects(:exit).never
@handler.process!
assert_equal :reload, @handler.when_ready
end
def test_interrupted_via_USR1_when_not_in_request
request = mock
FCGI.expects(:each).once.yields(request)
@handler.expects(:signal).times(2).returns('USR1')
@handler.expects(:exit_handler).never
@handler.expects(:reload!).never
@handler.expects(:close_connection).with(request).once
@handler.expects(:exit).never
@handler.process!
assert_nil @handler.when_ready
end
def test_restart_via_USR2_when_in_request
request = mock
FCGI.expects(:each).once.yields(request)
@handler.expects(:signal).times(2).returns('USR2')
@handler.expects(:exit_handler).never
@handler.expects(:reload!).never
@handler.expects(:close_connection).with(request).once
@handler.expects(:exit).never
@handler.expects(:restart!).once
@handler.process!
assert_equal :restart, @handler.when_ready
end
def test_interrupted_via_TERM
request = mock
FCGI.expects(:each).once.yields(request)
::Rack::Handler::FastCGI.expects(:serve).once.returns('TERM')
@handler.expects(:reload!).never
@handler.expects(:close_connection).never
@handler.process!
assert_nil @handler.when_ready
end
def test_runtime_exception_in_fcgi
error = RuntimeError.new('foo')
FCGI.expects(:each).times(2).raises(error)
@handler.expects(:dispatcher_error).with(error, regexp_matches(/^retrying/))
@handler.expects(:dispatcher_error).with(error, regexp_matches(/^stopping/))
@handler.process!
end
def test_runtime_error_in_dispatcher
request = mock
error = RuntimeError.new('foo')
FCGI.expects(:each).once.yields(request)
::Rack::Handler::FastCGI.expects(:serve).once.raises(error)
@handler.expects(:dispatcher_error).with(error, regexp_matches(/^unhandled/))
@handler.process!
end
def test_signal_exception_in_fcgi
error = SignalException.new('USR2')
FCGI.expects(:each).once.raises(error)
@handler.expects(:dispatcher_error).with(error, regexp_matches(/^stopping/))
@handler.process!
end
def test_signal_exception_in_dispatcher
request = mock
error = SignalException.new('USR2')
FCGI.expects(:each).once.yields(request)
::Rack::Handler::FastCGI.expects(:serve).once.raises(error)
@handler.expects(:dispatcher_error).with(error, regexp_matches(/^stopping/))
@handler.process!
end
end
class RailsFCGIHandlerPeriodicGCTest < Test::Unit::TestCase
def setup
@log = StringIO.new
end
def teardown
GC.enable
end
def test_normal_gc
@handler = RailsFCGIHandler.new(@log)
assert_nil @handler.gc_request_period
# When GC is enabled, GC.disable disables and returns false.
assert_equal false, GC.disable
end
def test_periodic_gc
@handler = RailsFCGIHandler.new(@log, 10)
assert_equal 10, @handler.gc_request_period
request = mock
FCGI.expects(:each).times(10).yields(request)
@handler.expects(:run_gc!).never
9.times { @handler.process! }
@handler.expects(:run_gc!).once
@handler.process!
assert_nil @handler.when_ready
end
end
end # uses_gem "fcgi"
end # exclude 1.9
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册