class RackRabbit::Server

Attributes

app[R]
config[R]
killed_pids[R]
logger[R]
server_pid[R]
signals[R]
worker_pids[R]

Public Class Methods

new(options) click to toggle source
# File lib/rack-rabbit/server.rb, line 23
def initialize(options)
  @config       = Config.new(options)
  @logger       = config.logger
  @server_pid   = $$
  @worker_pids  = []
  @killed_pids  = []
  @signals      = Signals.new
end

Public Instance Methods

check_pid() click to toggle source
# File lib/rack-rabbit/server.rb, line 207
def check_pid
  pidfile = config.pidfile
  if pidfile
    case pid_status(pidfile)
    when :running, :not_owned
      logger.fatal "A server is already running. Check #{pidfile}"
      exit(1)
    when :dead
      File.delete(pidfile)
    end
  end
end
daemonize() click to toggle source
DAEMONIZING, PID MANAGEMENT, and OUTPUT REDIRECTION
# File lib/rack-rabbit/server.rb, line 170
def daemonize
  exit if fork
  Process.setsid
  exit if fork
  Dir.chdir "/"
  redirect_output
  logger.master_pid = $$ if logger.respond_to?(:master_pid)  # inform logger of new master_pid so it can continue to distinguish between "SERVER" and "worker" in log preamble
end
kill_all_workers(sig) click to toggle source
# File lib/rack-rabbit/server.rb, line 132
def kill_all_workers(sig)
  kill_worker(sig, worker_pids.last) until worker_pids.empty?
end
kill_random_worker(sig) click to toggle source
# File lib/rack-rabbit/server.rb, line 128
def kill_random_worker(sig)
  kill_worker(sig, worker_pids.sample) # choose a random wpid
end
kill_worker(sig, wpid) click to toggle source
# File lib/rack-rabbit/server.rb, line 136
def kill_worker(sig, wpid)
  worker_pids.delete(wpid)
  killed_pids.push(wpid)
  Process.kill(sig, wpid)
end
load_app() click to toggle source
RACK APP HANDLING
# File lib/rack-rabbit/server.rb, line 250
def load_app
  inner_app, _options = RackRabbit.load_rack_app(config.rack_file)
  @app = Rack::Builder.new do
    use RackRabbit::Middleware::ProgramName
    run inner_app
  end.to_app
  logger.info "LOADED #{inner_app.name if inner_app.respond_to?(:name)} FROM #{config.rack_file}"
  @app
end
maintain_worker_count() click to toggle source
# File lib/rack-rabbit/server.rb, line 106
def maintain_worker_count
  unless shutting_down?
    diff = worker_pids.length - config.workers
    if diff > 0
      diff.times { kill_random_worker(:QUIT) }
    elsif diff < 0
      (-diff).times { spawn_worker }
    end
  end
end
manage_workers() click to toggle source
# File lib/rack-rabbit/server.rb, line 65
def manage_workers
  while true

    maintain_worker_count

    sig = signals.pop   # BLOCKS until there is a signal
    case sig

    when :INT  then shutdown(:INT)
    when :QUIT then shutdown(:QUIT)
    when :TERM then shutdown(:TERM)

    when :HUP
      reload

    when :CHLD
      reap_workers

    when :TTIN
      config.workers [config.max_workers, config.workers + 1].min

    when :TTOU
      config.workers [config.min_workers, config.workers - 1].max

    else
      raise RuntimeError, "unknown signal #{sig}"

    end

  end
end
pid_status(pidfile) click to toggle source
# File lib/rack-rabbit/server.rb, line 220
def pid_status(pidfile)
  return :exited unless File.exists?(pidfile)
  pid = ::File.read(pidfile).to_i
  return :dead if pid == 0
  Process.kill(0, pid)
  :running
rescue Errno::ESRCH
  :dead
rescue Errno::EPERM
  :not_owned
end
reap_workers() click to toggle source
# File lib/rack-rabbit/server.rb, line 142
def reap_workers
  while true
    wpid = Process.waitpid(-1, Process::WNOHANG)
    return if wpid.nil?
    worker_pids.delete(wpid)
    killed_pids.delete(wpid)
  end
  rescue Errno::ECHILD
end
redirect_output() click to toggle source
# File lib/rack-rabbit/server.rb, line 179
def redirect_output
  if logfile = config.logfile
    logfile = File.expand_path(logfile)
    FileUtils.mkdir_p(File.dirname(logfile), :mode => 0755)
    FileUtils.touch logfile
    File.chmod(0644, logfile)
    $stderr.reopen(logfile, 'a')
    $stdout.reopen($stderr)
    $stdout.sync = $stderr.sync = true
  else
    $stderr.reopen('/dev/null', 'a')
    $stdout.reopen($stderr)
  end
end
reload() click to toggle source
# File lib/rack-rabbit/server.rb, line 99
def reload
  logger.info "RELOADING"
  config.reload
  load_app if config.preload_app
  kill_all_workers(:QUIT)  # they will respawn automatically
end
run() click to toggle source
# File lib/rack-rabbit/server.rb, line 34
def run

  check_pid

  if config.daemonize
    daemonize
  elsif config.logfile
    redirect_output
  end

  write_pid

  logger.info "RUNNING #{config.app_id} (#{config.rack_file}) #{'DAEMONIZED' if config.daemonize}"
  logger.info "  rabbit   : #{config.rabbit}"
  logger.info "  exchange : #{config.exchange} (#{config.exchange_type})" if config.exchange
  logger.info "  queue    : #{config.queue}"                              if config.queue
  logger.info "  route    : #{config.routing_key}"                        if config.routing_key
  logger.info "  workers  : #{config.workers}"
  logger.info "  preload  : true"              if config.preload_app
  logger.info "  logfile  : #{config.logfile}" unless config.logfile.nil?
  logger.info "  pidfile  : #{config.pidfile}" unless config.pidfile.nil?

  load_app if config.preload_app

  trap_server_signals
  manage_workers

end
shutdown(sig) click to toggle source
# File lib/rack-rabbit/server.rb, line 154
def shutdown(sig)
  @shutting_down = true
  kill_all_workers(sig)
  Process.waitall
  logger.info "#{RackRabbit.friendly_signal(sig)} server"
  exit
end
shutting_down?() click to toggle source
# File lib/rack-rabbit/server.rb, line 162
def shutting_down?
  @shutting_down
end
spawn_worker() click to toggle source
# File lib/rack-rabbit/server.rb, line 117
def spawn_worker
  config.before_fork(self)
  worker_pids << fork do
    signals.close
    load_app unless config.preload_app
    worker = Worker.new(config, app)
    config.after_fork(self, worker)
    worker.run
  end
end
trap_server_signals() click to toggle source
SIGNAL HANDLING
# File lib/rack-rabbit/server.rb, line 236
def trap_server_signals

  [:HUP, :INT, :QUIT, :TERM, :CHLD, :TTIN, :TTOU].each do |sig|
    trap(sig) do
      signals.push(sig)
    end
  end

end
write_pid() click to toggle source
# File lib/rack-rabbit/server.rb, line 194
def write_pid
  pidfile = config.pidfile
  if pidfile
    begin
      File.open(pidfile, ::File::CREAT | ::File::EXCL | ::File::WRONLY){|f| f.write("#{Process.pid}") }
      at_exit { File.delete(pidfile) if File.exists?(pidfile) }
    rescue Errno::EEXIST
      check_pid
      retry
    end
  end
end