Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Added an EXPERIMENTAL gateway.cgi for getting high-speed performance …

…through vanilla CGI using a long-running, DRb-backed server in the background (using script/listener and script/tracker) #1603 [Nicholas Seckar]

git-svn-id: http://svn-commit.rubyonrails.org/rails/trunk@1676 5ecf4fe2-1ee6-0310-87b1-e25e094e27de
  • Loading branch information...
commit 9e4f5f33974b018fe4b6eba2766af263f8b06951 1 parent 00739de
David Heinemeier Hansson dhh authored
99 railties/bin/listener
View
@@ -0,0 +1,99 @@
+require "drb"
+ENV["RAILS_ENV"] = 'production'
+require "#{File.dirname(__FILE__)}/../config/environment.rb"
+require 'fcgi_handler'
+
+VERBOSE = false
+
+class Listener
+ include DRbUndumped
+ attr_accessor :tracker
+
+ def initialize(timeout = nil)
+ @timeout = timeout
+ @mutex = Mutex.new
+ @active = false
+
+ @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
+ end
+
+ def each_cgi(&block)
+ @cgi_block = block
+ loop do
+ @timeout ? sleep(@timeout) : sleep
+ die! unless @active
+ @active = false
+ end
+ end
+end
+
+class FakeOut < Struct.new(:contents)
+ def initialize
+ super("")
+ end
+ def write(str)
+ contents << str
+ end
+ def read!
+ c = contents
+ self.contents = ''
+ return c
+ 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 'ruby', __FILE__, 'start-listeners', tracker, (number - 1).to_s
+ end
+ end
+
+ l = Listener.new(90)
+ l.run(nil, tracker)
+end
106 railties/bin/tracker
View
@@ -0,0 +1,106 @@
+require "drb"
+
+VERBOSE = false
+
+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
+ 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
+ end
+ 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
+ end
+ @processed = false
+ 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 'ruby', listener_path, 'start-listeners', tracker_uri, n.to_s
+ 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
57 railties/dispatches/gateway.cgi
View
@@ -0,0 +1,57 @@
+#!/usr/bin/ruby
+
+# This is an experimental feature for getting high-speed CGI by using a long-running, DRb-backed server in the background
+
+require File.join(File.dirname(__FILE__), 'drb')
+require 'cgi'
+
+VERBOSE = false
+
+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 start_tracker
+ tracker_path = File.join(File.dirname(__FILE__), '../script/tracker')
+ fork do
+ Process.setsid
+ STDIN.reopen "/dev/null"
+ STDOUT.reopen "/dev/null", "a"
+
+ exec 'ruby', tracker_path, 'start', ConnectionUri
+ end
+
+ $stderr.puts "dispatch: waiting for tracker to start..." if VERBOSE
+ 10.times do
+ sleep 0.5
+ return if File.exists? SocketPath
+ 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
+end
+
+$stderr.puts "dispatch: attempting to contact tracker..." if VERBOSE
+tracker = DRbObject.new_with_uri(ConnectionUri)
+tracker.ping # Test connection
+
+$stdout.extend DRbUndumped
+$stdin.extend DRbUndumped
+
+DRb.start_service "drbunix:", $stdin
+$stderr.puts "dispatch: publishing stdin..." if VERBOSE
+
+$stderr.puts "dispatch: sending request to tracker" if VERBOSE
+puts tracker.process($stdin)
+
+$stdout.flush
+[$stdin, $stdout].each {|io| io.close}
+$stderr.puts "dispatch: finished..." if VERBOSE
+
+
Please sign in to comment.
Something went wrong with that request. Please try again.