Added fixed gateway script [Nicholas Seckar]

git-svn-id: http://svn-commit.rubyonrails.org/rails/trunk@1721 5ecf4fe2-1ee6-0310-87b1-e25e094e27de
上级 31702951
......@@ -137,7 +137,7 @@ task :copy_dispatches do
chmod 0755, "#{PKG_DESTINATION}/public/dispatch.fcgi"
copy_with_rewritten_ruby_path("dispatches/gateway.cgi", "#{PKG_DESTINATION}/public/gateway.cgi")
chmod 0644, "#{PKG_DESTINATION}/public/gateway.cgi"
chmod 0755, "#{PKG_DESTINATION}/public/gateway.cgi"
end
task :copy_html_files do
......
require "drb"
ENV["RAILS_ENV"] = 'production'
require "#{File.dirname(__FILE__)}/../config/environment.rb"
#!/usr/local/bin/ruby
require 'stringio'
require 'fileutils'
require 'fcgi_handler'
require 'rbconfig'
VERBOSE = false
def message(s)
$stderr.puts "listener: #{s}" if ENV && ENV["DEBUG_GATEWAY"]
end
class RemoteCGI < CGI
attr_accessor :stdinput, :stdoutput, :env_table
def initialize(env_table, input = nil, output = nil)
self.env_table = env_table
self.stdinput = input || StringIO.new
self.stdoutput = output || StringIO.new
super()
end
def out(stream) # Ignore the requested output stream
super(stdoutput)
end
end
class Listener
include DRbUndumped
attr_accessor :tracker
def initialize(timeout = nil)
@timeout = timeout
def initialize(timeout, socket_path)
@socket = File.expand_path(socket_path)
@mutex = Mutex.new
@active = false
@timeout = timeout
@handler = RailsFCGIHandler.new
@handler.extend DRbUndumped
@output = FakeOut.new
$stdout = @output
end
def inform_up(tracker_uri)
return unless tracker_uri
tracker = DRbObject.new_with_uri(tracker_uri)
tracker.register_listener self
@tracker = tracker
end
def inform_down
@tracker.remove_listener(self) if @tracker
end
def run(on_uri, tracker_uri)
on_uri ||= "drbunix:"
DRb.start_service(on_uri, self) # Start a server for us
inform_up tracker_uri
@handler.process!(self)
end
def die!
inform_down
Kernel.exit 0
end
def process(input)
$stderr.puts "listener: received request -- obtaining lock" if VERBOSE
@mutex.synchronize do
@active = true
$stderr.puts "listener: obtained -- swaping stdin" if VERBOSE
$stdin = input
cgi = CGI.new
$stderr.puts "listener: yielding to FCGI handler..." if VERBOSE
@cgi_block.call cgi
$stderr.puts "listener: handler finished, releasing control" if VERBOSE
return @output.read!
end
message 'opening socket'
DRb.start_service("drbunix:#{@socket}", self)
message 'entering process loop'
@handler.process! self
end
def each_cgi(&block)
@cgi_block = block
def each_cgi(&cgi_block)
@cgi_block = cgi_block
message 'entering idle loop'
loop do
@timeout ? sleep(@timeout) : sleep
sleep @timeout rescue nil
die! unless @active
@active = false
end
end
end
class FakeOut < Struct.new(:contents)
def initialize
super("")
end
def write(str)
contents << str
def process(env, input)
message 'received request'
@mutex.synchronize do
@active = true
message 'creating input stream'
input_stream = StringIO.new(input)
message 'building CGI instance'
cgi = RemoteCGI.new(eval(env), input_stream)
message 'yielding to fcgi handler'
@cgi_block.call cgi
message 'yield finished -- sending output'
cgi.stdoutput.seek(0)
output = cgi.stdoutput.read
return output
end
end
def read!
c = contents
self.contents = ''
return c
def die!
message 'shutting down'
DRb.stop_service
FileUtils.rm_f @socket
Kernel.exit 0
end
end
if ARGV.shift == 'start-listeners'
tracker = ARGV.shift
number = (ARGV.shift || '1').to_i
exit(0) if number.zero?
if number > 1
fork do
exec(
File.join(Config::CONFIG['bin_dir'], Config::CONFIG['RUBY_SO_NAME']),
__FILE__, 'start-listeners', tracker, (number - 1).to_s
)
end
end
l = Listener.new(90)
l.run(nil, tracker)
end
\ No newline at end of file
socket_path = ARGV.shift
timeout = (ARGV.shift || 90).to_i
Listener.new(timeout, socket_path)
\ No newline at end of file
require "drb"
require "rbconfig"
#!/usr/local/bin/ruby
VERBOSE = false
require 'drb'
require 'thread'
def message(s)
$stderr.puts "tracker: #{s}" if ENV && ENV["DEBUG_GATEWAY"]
end
class Tracker
include DRbUndumped
def initialize(timeout = 90, uri = nil)
@timeout = timeout
@waiting = []
@working = []
@waiting_mutex = Mutex.new
DRb.start_service(uri, self)
@uri = DRb.uri
end
def run
start_listener 3
sleep 3
background
end
def register_listener(listener)
@waiting.push listener
nil
end
def remove_listener(listener)
@waiting.delete listener
@working.delete listener
nil
def initialize(instances, socket_path)
@instances = instances
@socket = File.expand_path(socket_path)
@active = false
@listeners = []
@instances.times { @listeners << Mutex.new }
message "using #{@listeners.length} listeners"
message "opening socket at #{@socket}"
@service = DRb.start_service("drbunix://#{@socket}", self)
end
def with_listener
listener = @waiting.shift
unless listener
start_listener(2) unless @waiting.length + @working.length > 6
@waiting_mutex.synchronize do
10.times do
sleep 0.5
listener = @waiting.shift
break if listener
end
unless listener
($stderr.puts "Dropping request due to lack of listeners!!!" unless listener) if VERBOSE
return
end
message "listener requested"
mutex = has_lock = index = nil
3.times do
@listeners.each_with_index do |mutex, index|
has_lock = mutex.try_lock
break if has_lock
end
break if has_lock
sleep 0.05
end
@working << listener
yield listener
ensure
if listener
@working.delete listener
@waiting << listener
end
end
def background
loop do
@timeout ? sleep(@timeout) : sleep
unless @processed
$stderr.puts "Idle for #{@timeout} -- shutting down tracker." if VERBOSE
Kernel.exit 0
if has_lock
message "obtained listener #{index}"
@active = true
begin yield index
ensure
mutex.unlock
message "released listener #{index}"
end
@processed = false
else
message "dropping request because no listeners are available!"
end
end
def process(input)
output = nil
$stderr.puts "tracker: received request.. obtaining listener" if VERBOSE
with_listener do |listener|
$stderr.puts "tracker: obtained -- forwarding request to listener.." if VERBOSE
@processed = true
output = listener.process(input)
$stderr.puts "tracker: listener released control." if VERBOSE
end
return output
end
def start_listener(n = 1)
tracker_uri = @uri
listener_path = File.join(File.dirname(__FILE__), 'listener')
fork do
exec(
File.join(Config::CONFIG['bin_dir'], Config::CONFIG['RUBY_SO_NAME']),
listener_path, 'start-listeners', tracker_uri, n.to_s
)
def background(check_interval = nil)
if check_interval
loop do
sleep check_interval
message "Idle for #{check_interval}, shutting down" unless @active
@active = false
Kernel.exit 0
end
else DRb.thread.join
end
end
def ping
true
end
end
if ARGV.first == "start"
tracker = Tracker.new(90, ARGV[1])
socket = (/druby:([^?]*)\?/ =~ ARGV[1]) ? $1 : nil
require 'fileutils' if socket
begin tracker.run
ensure
FileUtils.rm_f(socket) if socket
end
end
socket_path = ARGV.shift
instances = ARGV.shift.to_i
t = Tracker.new(instances, socket_path)
t.background(ARGV.first ? ARGV.shift.to_i : 90)
\ No newline at end of file
#!/usr/bin/ruby
# This is an experimental feature for getting high-speed CGI by using a long-running, DRb-backed server in the background
#!/usr/local/bin/ruby
require 'drb'
require 'cgi'
require 'rbconfig'
VERBOSE = false
# This file includes an experimental gateway CGI implementation. It will work
# only on platforms which support both fork and sockets.
#
# To enable it edit public/.htaccess and replace dispatch.cgi with gateway.cgi.
#
# Next, create the directory log/drb_gateway and grant the apache user rw access
# to said directory.
#
# On the next request to your server, the gateway tracker should start up, along
# with a few listener processes. This setup should provide you with much better
# speeds than dispatch.cgi.
#
# Keep in mind that the first request made to the server will be slow, as the
# tracker and listeners will have to load. Also, the tracker and listeners will
# shutdown after a period if inactivity. You can set this value below -- the
# default is 90 seconds.
TrackerSocket = File.expand_path(File.join(File.dirname(__FILE__), '../log/drb_gateway/tracker.sock'))
DieAfter = 90 # Seconds
Listeners = 3
AppName = File.split(File.expand_path(File.join(__FILE__, '..'))).last
SocketPath = File.expand_path(File.join(File.dirname(__FILE__), '../log/drb_gateway.sock'))
ConnectionUri = "drbunix:#{SocketPath}"
attempted_start = false
def message(s)
$stderr.puts "gateway.cgi: #{s}" if ENV && ENV["DEBUG_GATEWAY"]
end
def start_tracker
tracker_path = File.join(File.dirname(__FILE__), '../script/tracker')
def listener_socket(number)
File.expand_path(File.join(File.dirname(__FILE__), "../log/drb_gateway/listener_#{number}.sock"))
end
unless File.exists? TrackerSocket
message "Starting tracker and #{Listeners} listeners"
fork do
Process.setsid
STDIN.reopen "/dev/null"
STDOUT.reopen "/dev/null", "a"
exec(File.join(Config::CONFIG['bin_dir'], Config::CONFIG['RUBY_SO_NAME']), tracker_path, 'start', ConnectionUri)
root = File.expand_path(File.dirname(__FILE__) + '/..')
message "starting tracker"
fork do
ARGV.clear
ARGV << TrackerSocket << Listeners.to_s << DieAfter.to_s
load File.join(root, 'script', 'tracker')
end
message "starting listeners"
require File.join(root, 'config/environment.rb')
Listeners.times do |number|
fork do
ARGV.clear
ARGV << listener_socket(number) << DieAfter.to_s
load File.join(root, 'script', 'listener')
end
end
end
$stderr.puts "dispatch: waiting for tracker to start..." if VERBOSE
message "waiting for tracker and listener to arise..."
ready = false
10.times do
sleep 0.5
return if File.exists? SocketPath
break if (ready = File.exists?(TrackerSocket) && File.exists?(listener_socket(0)))
end
$stderr.puts "Can't start tracker!!! Dropping request!"
Kernel.exit 1
end
unless File.exists?(SocketPath)
$stderr.puts "tracker not running: starting it..." if VERBOSE
start_tracker
if ready
message "tracker and listener are ready"
else
message "Waited 5 seconds, listener and tracker not ready... dropping request"
Kernel.exit 1
end
end
$stderr.puts "dispatch: attempting to contact tracker..." if VERBOSE
tracker = DRbObject.new_with_uri(ConnectionUri)
tracker.ping # Test connection
DRb.start_service
$stdout.extend DRbUndumped
$stdin.extend DRbUndumped
DRb.start_service "drbunix:", $stdin
$stderr.puts "dispatch: publishing stdin..." if VERBOSE
message "connecting to tracker"
tracker = DRbObject.new_with_uri("drbunix:#{TrackerSocket}")
$stderr.puts "dispatch: sending request to tracker" if VERBOSE
puts tracker.process($stdin)
input = $stdin.read
$stdin.close
$stdout.flush
[$stdin, $stdout].each {|io| io.close}
$stderr.puts "dispatch: finished..." if VERBOSE
env = ENV.inspect
output = nil
tracker.with_listener do |number|
message "connecting to listener #{number}"
socket = listener_socket(number)
listener = DRbObject.new_with_uri("drbunix:#{socket}")
output = listener.process(env, input)
message "listener #{number} has finished, writing output"
end
$stdout.write output
$stdout.flush
$stdout.close
\ No newline at end of file
......@@ -60,6 +60,7 @@
<li>See all the tests run by running <code>rake</code>.
<li>Develop your Rails application!
<li>Setup Apache with <a href="http://www.fastcgi.com">FastCGI</a> (and <a href="http://raa.ruby-lang.org/list.rhtml?name=fcgi">Ruby bindings</a>), if you need better performance
<li>Remove the dispatches you don't use (so if you're on FastCGI, delete/move dispatch.rb, dispatch.cgi and gateway.cgi)</li>
</ol>
<p>
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册