2006-02-21 18:31:05 -05:00
|
|
|
###############################################
|
2006-02-22 12:10:19 -05:00
|
|
|
# mongrel_rails_svc
|
2006-02-21 18:31:05 -05:00
|
|
|
#
|
|
|
|
# This is where Win32::Daemon resides.
|
|
|
|
###############################################
|
|
|
|
require 'rubygems'
|
2006-03-27 01:10:07 -05:00
|
|
|
require 'mongrel'
|
2006-02-28 02:04:41 -05:00
|
|
|
require 'mongrel/rails'
|
2006-02-21 18:31:05 -05:00
|
|
|
require 'optparse'
|
|
|
|
require 'win32/service'
|
|
|
|
|
2006-02-26 21:22:18 -05:00
|
|
|
# We need to use OpenProcess and SetProcessAffinityMask on WinNT/2K/XP for
|
|
|
|
# binding the process to each cpu.
|
|
|
|
# Kernel32 Module Just for Win32 :D
|
|
|
|
require 'dl/win32'
|
|
|
|
|
|
|
|
module Kernel32
|
|
|
|
[
|
|
|
|
%w/OpenProcess LLL L/,
|
|
|
|
%w/SetProcessAffinityMask LL L/,
|
|
|
|
].each do |fn|
|
|
|
|
const_set fn[0].intern, Win32API.new('kernel32.dll', *fn)
|
|
|
|
end
|
|
|
|
|
|
|
|
PROCESS_ALL_ACCESS = 0x1f0fff
|
|
|
|
|
|
|
|
module_function
|
|
|
|
|
|
|
|
def set_affinity(pid, cpu)
|
|
|
|
handle = OpenProcess.call(PROCESS_ALL_ACCESS, 0, pid)
|
|
|
|
|
|
|
|
# CPU mask is a bit weird, hehehe :)
|
|
|
|
# default mask for CPU 1
|
|
|
|
mask = 1
|
|
|
|
mask = %w{1 2 4 8 16 32 64 128}[cpu.to_i - 1] if cpu.to_i.between?(1, 8)
|
|
|
|
|
|
|
|
SetProcessAffinityMask.call(handle, mask.to_i)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
# End Kernel32 Module
|
|
|
|
|
2006-02-21 18:31:05 -05:00
|
|
|
DEBUG_LOG_FILE = File.expand_path(File.dirname(__FILE__) + '/debug.log')
|
2006-02-22 20:16:51 -05:00
|
|
|
DEBUG_THREAD_LOG_FILE = File.expand_path(File.dirname(__FILE__) + '/debug_thread.log')
|
2006-02-21 18:31:05 -05:00
|
|
|
|
2006-02-28 02:04:41 -05:00
|
|
|
def dbg(msg)
|
|
|
|
File.open(DEBUG_LOG_FILE,"a+") { |f| f.puts("#{Time.now} - #{msg}") }
|
|
|
|
end
|
2006-02-21 18:31:05 -05:00
|
|
|
|
2006-02-28 02:04:41 -05:00
|
|
|
def dbg_th(msg)
|
|
|
|
File.open(DEBUG_THREAD_LOG_FILE,"a+") { |f| f.puts("#{Time.now} - #{msg}") }
|
2006-02-21 18:31:05 -05:00
|
|
|
end
|
|
|
|
|
2006-02-22 20:16:51 -05:00
|
|
|
# This class encapsulate the handler registering, http_server and working thread
|
|
|
|
# Is standalone, so using MongrelRails in your app get everything runnig
|
|
|
|
# (in case you don't want use mongrel_rails script)
|
|
|
|
class MongrelRails
|
2006-02-21 18:31:05 -05:00
|
|
|
def initialize(ip, port, rails_root, docroot, environment, mime_map, num_procs, timeout)
|
2006-02-28 02:04:41 -05:00
|
|
|
dbg "mongrelrails_initialize entered"
|
2006-02-21 18:31:05 -05:00
|
|
|
|
|
|
|
@ip = ip
|
|
|
|
@port = port
|
|
|
|
@rails_root = rails_root
|
|
|
|
@docroot = docroot
|
|
|
|
@environment = environment
|
|
|
|
@mime_map = mime_map
|
|
|
|
@num_procs = num_procs
|
|
|
|
@timeout = timeout
|
|
|
|
|
2006-02-28 02:04:41 -05:00
|
|
|
dbg "mongrelrails_initialize left"
|
2006-02-21 18:31:05 -05:00
|
|
|
end
|
2006-02-22 20:16:51 -05:00
|
|
|
|
2006-02-26 21:22:18 -05:00
|
|
|
def delayed_initialize
|
2006-02-28 02:04:41 -05:00
|
|
|
dbg "delayed_initialize entered"
|
2006-03-27 01:10:07 -05:00
|
|
|
|
2006-02-22 20:16:51 -05:00
|
|
|
@rails = configure_rails
|
|
|
|
|
|
|
|
# start up mongrel with the right configurations
|
|
|
|
@server = Mongrel::HttpServer.new(@ip, @port, @num_procs.to_i, @timeout.to_i)
|
|
|
|
@server.register("/", @rails)
|
2006-03-27 01:10:07 -05:00
|
|
|
|
2006-02-28 02:04:41 -05:00
|
|
|
dbg "delayed_initialize left"
|
2006-03-27 01:10:07 -05:00
|
|
|
|
2006-02-22 20:16:51 -05:00
|
|
|
end
|
|
|
|
|
2006-02-21 18:31:05 -05:00
|
|
|
def load_mime_map
|
2006-02-28 02:04:41 -05:00
|
|
|
dbg "load_mime_map entered"
|
2006-02-21 18:31:05 -05:00
|
|
|
|
|
|
|
mime = {}
|
|
|
|
|
|
|
|
# configure any requested mime map
|
|
|
|
if @mime_map
|
|
|
|
puts "Loading additional MIME types from #@mime_map"
|
|
|
|
mime.merge!(YAML.load_file(@mime_map))
|
|
|
|
|
|
|
|
# check all the mime types to make sure they are the right format
|
|
|
|
mime.each {|k,v| puts "WARNING: MIME type #{k} must start with '.'" if k.index(".") != 0 }
|
|
|
|
end
|
|
|
|
|
2006-02-28 02:04:41 -05:00
|
|
|
dbg "load_mime_map left"
|
2006-02-21 18:31:05 -05:00
|
|
|
|
|
|
|
return mime
|
|
|
|
end
|
|
|
|
|
|
|
|
def configure_rails
|
2006-02-28 02:04:41 -05:00
|
|
|
dbg "configure_rails entered"
|
2006-02-21 18:31:05 -05:00
|
|
|
|
|
|
|
Dir.chdir(@rails_root)
|
|
|
|
|
2006-03-27 01:10:07 -05:00
|
|
|
|
2006-02-21 18:31:05 -05:00
|
|
|
ENV['RAILS_ENV'] = @environment
|
2006-03-27 01:10:07 -05:00
|
|
|
require 'config/environment'
|
2006-02-21 18:31:05 -05:00
|
|
|
|
|
|
|
# configure the rails handler
|
2006-03-27 01:10:07 -05:00
|
|
|
rails = Mongrel::Rails::RailsHandler.new(@docroot, load_mime_map)
|
2006-02-21 18:31:05 -05:00
|
|
|
|
2006-02-28 02:04:41 -05:00
|
|
|
dbg "configure_rails left"
|
2006-02-21 18:31:05 -05:00
|
|
|
|
|
|
|
return rails
|
|
|
|
end
|
|
|
|
|
2006-02-22 20:16:51 -05:00
|
|
|
def start_serve
|
2006-03-27 01:10:07 -05:00
|
|
|
begin
|
|
|
|
dbg "start_serve entered"
|
|
|
|
|
|
|
|
@runner = Thread.new do
|
|
|
|
dbg_th "runner_thread suspended"
|
|
|
|
Thread.stop
|
|
|
|
|
|
|
|
dbg_th "runner_thread resumed"
|
|
|
|
dbg_th "runner_thread acceptor.join"
|
|
|
|
@server.acceptor.join
|
|
|
|
end
|
2006-02-22 20:16:51 -05:00
|
|
|
|
2006-03-27 01:10:07 -05:00
|
|
|
dbg "server.run"
|
|
|
|
@server.run
|
|
|
|
|
|
|
|
dbg "runner.run"
|
|
|
|
@runner.run
|
|
|
|
|
|
|
|
dbg "start_serve left"
|
|
|
|
rescue
|
|
|
|
dbg "ERROR: #$!\r\n"
|
|
|
|
dbg $!.backtrace.join("\r\n")
|
2006-02-22 20:16:51 -05:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
def stop_serve
|
2006-02-28 02:04:41 -05:00
|
|
|
dbg "stop_serve entered"
|
2006-02-22 20:16:51 -05:00
|
|
|
|
|
|
|
if @runner.alive?
|
2006-02-28 02:04:41 -05:00
|
|
|
dbg "killing thread"
|
2006-02-22 20:16:51 -05:00
|
|
|
@runner.kill
|
|
|
|
end
|
|
|
|
|
|
|
|
@server.stop
|
|
|
|
|
2006-02-28 02:04:41 -05:00
|
|
|
dbg "stop_serve left"
|
2006-02-22 20:16:51 -05:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
class RailsDaemon < Win32::Daemon
|
|
|
|
def initialize(rails)
|
2006-02-28 02:04:41 -05:00
|
|
|
dbg "daemon_initialize entered"
|
2006-02-22 20:16:51 -05:00
|
|
|
|
|
|
|
@rails = rails
|
|
|
|
|
2006-02-28 02:04:41 -05:00
|
|
|
dbg "daemon_initialize left"
|
2006-02-22 20:16:51 -05:00
|
|
|
end
|
|
|
|
|
2006-02-21 18:31:05 -05:00
|
|
|
def service_init
|
2006-02-28 02:04:41 -05:00
|
|
|
dbg "service_init entered"
|
2006-02-21 18:31:05 -05:00
|
|
|
|
2006-02-26 21:22:18 -05:00
|
|
|
@rails.delayed_initialize
|
2006-02-21 18:31:05 -05:00
|
|
|
|
2006-02-28 02:04:41 -05:00
|
|
|
dbg "service_init left"
|
2006-02-21 18:31:05 -05:00
|
|
|
end
|
|
|
|
|
|
|
|
def service_main
|
2006-02-28 02:04:41 -05:00
|
|
|
dbg "service_main entered"
|
2006-02-21 18:31:05 -05:00
|
|
|
|
2006-02-28 02:04:41 -05:00
|
|
|
dbg "rails.start_serve"
|
2006-02-22 20:16:51 -05:00
|
|
|
@rails.start_serve
|
2006-02-21 18:31:05 -05:00
|
|
|
|
2006-02-28 02:04:41 -05:00
|
|
|
dbg "while RUNNING"
|
2006-02-21 18:31:05 -05:00
|
|
|
while state == RUNNING
|
|
|
|
sleep 1
|
|
|
|
end
|
2006-02-28 02:04:41 -05:00
|
|
|
dbg "state !RUNNING"
|
2006-02-21 18:31:05 -05:00
|
|
|
|
2006-02-28 02:04:41 -05:00
|
|
|
dbg "rails.stop_serve"
|
2006-02-22 20:16:51 -05:00
|
|
|
@rails.stop_serve
|
|
|
|
|
2006-02-28 02:04:41 -05:00
|
|
|
dbg "service_main left"
|
2006-02-21 18:31:05 -05:00
|
|
|
end
|
|
|
|
|
|
|
|
def service_stop
|
2006-02-28 02:04:41 -05:00
|
|
|
dbg "service_stop entered"
|
2006-02-22 20:16:51 -05:00
|
|
|
|
2006-02-28 02:04:41 -05:00
|
|
|
dbg "service_stop left"
|
2006-02-21 18:31:05 -05:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
|
2006-03-27 01:10:07 -05:00
|
|
|
begin
|
|
|
|
if ARGV[0] == 'service'
|
|
|
|
ARGV.shift
|
|
|
|
|
|
|
|
# default options
|
|
|
|
OPTIONS = {
|
|
|
|
:rails_root => Dir.pwd,
|
|
|
|
:environment => 'production',
|
|
|
|
:ip => '0.0.0.0',
|
|
|
|
:port => 3000,
|
|
|
|
:mime_map => nil,
|
|
|
|
:num_procs => 1024,
|
|
|
|
:timeout => 0,
|
|
|
|
:cpu => nil
|
|
|
|
}
|
|
|
|
|
|
|
|
ARGV.options do |opts|
|
|
|
|
opts.on('-r', '--root PATH', "Set the root path where your rails app resides.") { |OPTIONS[:rails_root]| }
|
|
|
|
opts.on('-e', '--environment ENV', "Rails environment to run as. (default: production)") { |OPTIONS[:environment]| }
|
|
|
|
opts.on('-b', '--binding ADDR', "Address to bind to") { |OPTIONS[:ip]| }
|
|
|
|
opts.on('-p', '--port PORT', "Which port to bind to") { |OPTIONS[:port]| }
|
|
|
|
opts.on('-m', '--mime PATH', "A YAML file that lists additional MIME types") { |OPTIONS[:mime_map]| }
|
|
|
|
opts.on('-P', '--num-procs INT', "Number of processor threads to use") { |OPTIONS[:num_procs]| }
|
|
|
|
opts.on('-t', '--timeout SECONDS', "Timeout all requests after SECONDS time") { |OPTIONS[:timeout]| }
|
|
|
|
opts.on('-c', '--cpu CPU', "Bind the process to specific cpu") { |OPTIONS[:cpu]| }
|
|
|
|
|
|
|
|
opts.parse!
|
|
|
|
end
|
|
|
|
|
|
|
|
#expand RAILS_ROOT
|
|
|
|
OPTIONS[:rails_root] = File.expand_path(OPTIONS[:rails_root])
|
|
|
|
|
|
|
|
OPTIONS[:docroot] = File.expand_path(OPTIONS[:rails_root] + '/public')
|
|
|
|
|
|
|
|
# We must bind to a specific cpu?
|
|
|
|
if OPTIONS[:cpu]
|
|
|
|
Kernel32.set_affinity(Process.pid, OPTIONS[:cpu])
|
|
|
|
end
|
|
|
|
|
|
|
|
rails = MongrelRails.new(OPTIONS[:ip], OPTIONS[:port], OPTIONS[:rails_root], OPTIONS[:docroot], OPTIONS[:environment], OPTIONS[:mime_map], OPTIONS[:num_procs].to_i, OPTIONS[:timeout].to_i)
|
|
|
|
rails_svc = RailsDaemon.new(rails)
|
|
|
|
rails_svc.mainloop
|
|
|
|
|
|
|
|
elsif ARGV[0] == 'debug'
|
|
|
|
ARGV.shift
|
|
|
|
|
|
|
|
# default options
|
|
|
|
OPTIONS = {
|
|
|
|
:rails_root => Dir.pwd,
|
|
|
|
:environment => 'production',
|
|
|
|
:ip => '0.0.0.0',
|
|
|
|
:port => 3000,
|
|
|
|
:mime_map => nil,
|
|
|
|
:num_procs => 20,
|
|
|
|
:timeout => 120,
|
|
|
|
:cpu => nil
|
|
|
|
}
|
|
|
|
|
|
|
|
ARGV.options do |opts|
|
|
|
|
opts.on('-r', '--root PATH', "Set the root path where your rails app resides.") { |OPTIONS[:rails_root]| }
|
|
|
|
opts.on('-e', '--environment ENV', "Rails environment to run as.") { |OPTIONS[:environment]| }
|
|
|
|
opts.on('-b', '--binding ADDR', "Address to bind to") { |OPTIONS[:ip]| }
|
|
|
|
opts.on('-p', '--port PORT', "Which port to bind to") { |OPTIONS[:port]| }
|
|
|
|
opts.on('-m', '--mime PATH', "A YAML file that lists additional MIME types") { |OPTIONS[:mime_map]| }
|
|
|
|
opts.on('-P', '--num-procs INT', "Number of processor threads to use") { |OPTIONS[:num_procs]| }
|
|
|
|
opts.on('-t', '--timeout SECONDS', "Timeout all requests after SECONDS time") { |OPTIONS[:timeout]| }
|
|
|
|
opts.on('-c', '--cpu CPU', "Bind the process to specific cpu") { |OPTIONS[:cpu]| }
|
|
|
|
|
|
|
|
opts.parse!
|
|
|
|
end
|
|
|
|
|
|
|
|
#expand RAILS_ROOT
|
|
|
|
OPTIONS[:rails_root] = File.expand_path(OPTIONS[:rails_root])
|
|
|
|
|
|
|
|
OPTIONS[:docroot] = File.expand_path(OPTIONS[:rails_root] + '/public')
|
|
|
|
|
|
|
|
# We must bind to a specific cpu?
|
|
|
|
if OPTIONS[:cpu]
|
|
|
|
Kernel32.set_affinity(Process.pid, OPTIONS[:cpu])
|
|
|
|
end
|
|
|
|
|
|
|
|
rails = MongrelRails.new(OPTIONS[:ip], OPTIONS[:port], OPTIONS[:rails_root], OPTIONS[:docroot], OPTIONS[:environment], OPTIONS[:mime_map], OPTIONS[:num_procs].to_i, OPTIONS[:timeout].to_i)
|
|
|
|
rails.delayed_initialize
|
|
|
|
rails.start_serve
|
|
|
|
|
|
|
|
begin
|
|
|
|
sleep
|
|
|
|
rescue Interrupt
|
|
|
|
dbg "ERROR: #$!\r\n"
|
|
|
|
dbg $!.backtrace.join("\r\n")
|
|
|
|
puts "graceful shutdown?"
|
|
|
|
end
|
|
|
|
|
|
|
|
begin
|
|
|
|
rails.stop_serve
|
|
|
|
rescue
|
|
|
|
dbg "ERROR: #$!\r\n"
|
|
|
|
dbg $!.backtrace.join("\r\n")
|
|
|
|
end
|
|
|
|
end
|
|
|
|
rescue
|
|
|
|
dbg "ERROR: #$!\r\n"
|
|
|
|
dbg $!.backtrace.join("\r\n")
|
2006-02-22 20:16:51 -05:00
|
|
|
end
|