Parent

Unicorn::HttpServer

This is the process manager of Unicorn. This manages worker processes which in turn handle the I/O and application process. Listener sockets are started in the master process and shared with forked worker children.

Constants

START_CTX

We populate this at startup so we can figure out how to reexecute and upgrade the currently running instance of Unicorn This Hash is considered a stable interface and changing its contents will allow you to switch between different installations of Unicorn or even different installations of the same applications without downtime. Keys of this constant Hash are described as follows:

  • 0 - the path to the unicorn/unicorn_rails executable

  • :argv - a deep copy of the ARGV array the executable originally saw

  • :cwd - the working directory of the application, this is where

you originally started Unicorn.

To change your unicorn executable to a different path without downtime, you can set the following in your Unicorn config file, HUP and then continue with the traditional USR2 + QUIT upgrade steps:

Unicorn::HttpServer::START_CTX[0] = "/home/bofh/1.9.2/bin/unicorn"

Attributes

after_fork[RW]
app[RW]
before_exec[RW]
before_fork[RW]
config[RW]
init_listeners[RW]
listener_opts[RW]
logger[R]
master_pid[RW]
orig_app[RW]
pid[R]
preload_app[RW]
ready_pipe[RW]
reexec_pid[RW]
request[RW]
timeout[RW]
user[RW]
worker_processes[RW]

Public Class Methods

new(app, options = {}) click to toggle source

Creates a working server on host:port (strange things happen if port isn’t a Number). Use HttpServer::run to start the server and HttpServer.run.join to join the thread that’s processing incoming requests on the socket.

# File lib/unicorn/http_server.rb, line 91
def initialize(app, options = {})
  @app = app
  @request = Unicorn::HttpRequest.new
  self.reexec_pid = 0
  options = options.dup
  self.ready_pipe = options.delete(:ready_pipe)
  self.init_listeners = options[:listeners] ? options[:listeners].dup : []
  options[:use_defaults] = true
  self.config = Unicorn::Configurator.new(options)
  self.listener_opts = {}

  # we try inheriting listeners first, so we bind them later.
  # we don't write the pid file until we've bound listeners in case
  # unicorn was started twice by mistake.  Even though our #pid= method
  # checks for stale/existing pid files, race conditions are still
  # possible (and difficult/non-portable to avoid) and can be likely
  # to clobber the pid if the second start was in quick succession
  # after the first, so we rely on the listener binding to fail in
  # that case.  Some tests (in and outside of this source tree) and
  # monitoring tools may also rely on pid files existing before we
  # attempt to connect to the listener(s)
  config.commit!(self, :skip => [:listeners, :pid])
  self.orig_app = app
end

Public Instance Methods

client_body_buffer_size() click to toggle source
# File lib/unicorn/http_server.rb, line 364
def client_body_buffer_size
  Unicorn::TeeInput.client_body_buffer_size
end
client_body_buffer_size=(bytes) click to toggle source
# File lib/unicorn/http_server.rb, line 368
def client_body_buffer_size=(bytes)
  Unicorn::TeeInput.client_body_buffer_size = bytes
end
join() click to toggle source

monitors children and receives signals forever (or until a termination signal is sent). This handles signals one-at-a-time time and we’ll happily drop signals in case somebody is signalling us too often.

# File lib/unicorn/http_server.rb, line 273
def join
  respawn = true
  last_check = Time.now

  proc_name 'master'
  logger.info "master process ready" # test_exec.rb relies on this message
  if ready_pipe
    ready_pipe.syswrite($$.to_s)
    ready_pipe.close rescue nil
    self.ready_pipe = nil
  end
  begin
    reap_all_workers
    case SIG_QUEUE.shift
    when nil
      # avoid murdering workers after our master process (or the
      # machine) comes out of suspend/hibernation
      if (last_check + @timeout) >= (last_check = Time.now)
        sleep_time = murder_lazy_workers
      else
        # wait for workers to wakeup on suspend
        sleep_time = @timeout/2.0 + 1
      end
      maintain_worker_count if respawn
      master_sleep(sleep_time)
    when :QUIT # graceful shutdown
      break
    when :TERM, :INT # immediate shutdown
      stop(false)
      break
    when :USR1 # rotate logs
      logger.info "master reopening logs..."
      Unicorn::Util.reopen_logs
      logger.info "master done reopening logs"
      kill_each_worker(:USR1)
    when :USR2 # exec binary, stay alive in case something went wrong
      reexec
    when :WINCH
      if Process.ppid == 1 || Process.getpgrp != $$
        respawn = false
        logger.info "gracefully stopping all workers"
        kill_each_worker(:QUIT)
        self.worker_processes = 0
      else
        logger.info "SIGWINCH ignored because we're not daemonized"
      end
    when :TTIN
      respawn = true
      self.worker_processes += 1
    when :TTOU
      self.worker_processes -= 1 if self.worker_processes > 0
    when :HUP
      respawn = true
      if config.config_file
        load_config!
      else # exec binary and exit if there's no config file
        logger.info "config_file not present, reexecuting binary"
        reexec
      end
    end
  rescue Errno::EINTR
  rescue => e
    logger.error "Unhandled master loop exception #{e.inspect}."
    logger.error e.backtrace.join("\n")
  end while true
  stop # gracefully shutdown all workers on our way out
  logger.info "master complete"
  unlink_pid_safe(pid) if pid
end
listen(address, opt = {}.merge(listener_opts[address] || {})) click to toggle source

add a given address to the listeners set, idempotently Allows workers to add a private, per-process listener via the after_fork hook. Very useful for debugging and testing. :tries may be specified as an option for the number of times to retry, and :delay may be specified as the time in seconds to delay between retries. A negative value for :tries indicates the listen will be retried indefinitely, this is useful when workers belonging to different masters are spawned during a transparent upgrade.

# File lib/unicorn/http_server.rb, line 240
def listen(address, opt = {}.merge(listener_opts[address] || {}))
  address = config.expand_addr(address)
  return if String === address && listener_names.include?(address)

  delay = opt[:delay] || 0.5
  tries = opt[:tries] || 5
  begin
    io = bind_listen(address, opt)
    unless Kgio::TCPServer === io || Kgio::UNIXServer === io
      IO_PURGATORY << io
      io = server_cast(io)
    end
    logger.info "listening on addr=#{sock_name(io)} fd=#{io.fileno}"
    LISTENERS << io
    io
  rescue Errno::EADDRINUSE => err
    logger.error "adding listener failed addr=#{address} (in use)"
    raise err if tries == 0
    tries -= 1
    logger.error "retrying in #{delay} seconds "                     "(#{tries < 0 ? 'infinite' : tries} tries left)"
    sleep(delay)
    retry
  rescue => err
    logger.fatal "error adding listener addr=#{address}"
    raise err
  end
end
listeners=(listeners) click to toggle source

replaces current listener set with listeners. This will close the socket if it will not exist in the new listener set

# File lib/unicorn/http_server.rb, line 166
def listeners=(listeners)
  cur_names, dead_names = [], []
  listener_names.each do |name|
    if // == name[0]
      # mark unlinked sockets as dead so we can rebind them
      (File.socket?(name) ? cur_names : dead_names) << name
    else
      cur_names << name
    end
  end
  set_names = listener_names(listeners)
  dead_names.concat(cur_names - set_names).uniq!

  LISTENERS.delete_if do |io|
    if dead_names.include?(sock_name(io))
      IO_PURGATORY.delete_if do |pio|
        pio.fileno == io.fileno && (pio.close rescue nil).nil? # true
      end
      (io.close rescue nil).nil? # true
    else
      set_server_sockopt(io, listener_opts[sock_name(io)])
      false
    end
  end

  (set_names - cur_names).each { |addr| listen(addr) }
end
logger=(obj) click to toggle source
# File lib/unicorn/http_server.rb, line 197
def logger=(obj)
  Unicorn::HttpRequest::DEFAULTS["rack.logger"] = @logger = obj
end
pid=(path) click to toggle source

sets the path for the PID file of the master process

# File lib/unicorn/http_server.rb, line 202
def pid=(path)
  if path
    if x = valid_pid?(path)
      return path if pid && path == pid && x == $$
      if x == reexec_pid && pid =~ /\.oldbin\z/
        logger.warn("will not set pid=#{path} while reexec-ed "                       "child is running PID:#{x}")
        return
      end
      raise ArgumentError, "Already running on PID:#{x} "                               "(or pid=#{path} is stale)"
    end
  end
  unlink_pid_safe(pid) if pid

  if path
    fp = begin
      tmp = "#{File.dirname(path)}/#{rand}.#$$"
      File.open(tmp, File::RDWR|File::CREAT|File::EXCL, 0644)
    rescue Errno::EEXIST
      retry
    end
    fp.syswrite("#$$\n")
    File.rename(fp.path, path)
    fp.close
  end
  @pid = path
end
rewindable_input() click to toggle source
# File lib/unicorn/http_server.rb, line 355
def rewindable_input
  Unicorn::HttpRequest.input_class.method_defined?(:rewind)
end
rewindable_input=(bool) click to toggle source
# File lib/unicorn/http_server.rb, line 359
def rewindable_input=(bool)
  Unicorn::HttpRequest.input_class = bool ?
                              Unicorn::TeeInput : Unicorn::StreamInput
end
start() click to toggle source

Runs the thing. Returns self so you can run join on it

# File lib/unicorn/http_server.rb, line 117
def start
  BasicSocket.do_not_reverse_lookup = true

  # inherit sockets from parents, they need to be plain Socket objects
  # before they become Kgio::UNIXServer or Kgio::TCPServer
  inherited = ENV['UNICORN_FD'].to_s.split(/,/).map do |fd|
    io = Socket.for_fd(fd.to_i)
    set_server_sockopt(io, listener_opts[sock_name(io)])
    IO_PURGATORY << io
    logger.info "inherited addr=#{sock_name(io)} fd=#{fd}"
    server_cast(io)
  end

  config_listeners = config[:listeners].dup
  LISTENERS.replace(inherited)

  # we start out with generic Socket objects that get cast to either
  # Kgio::TCPServer or Kgio::UNIXServer objects; but since the Socket
  # objects share the same OS-level file descriptor as the higher-level
  # *Server objects; we need to prevent Socket objects from being
  # garbage-collected
  config_listeners -= listener_names
  if config_listeners.empty? && LISTENERS.empty?
    config_listeners << Unicorn::Const::DEFAULT_LISTEN
    init_listeners << Unicorn::Const::DEFAULT_LISTEN
    START_CTX[:argv] << "-l#{Unicorn::Const::DEFAULT_LISTEN}"
  end
  config_listeners.each { |addr| listen(addr) }
  raise ArgumentError, "no listeners" if LISTENERS.empty?

  # this pipe is used to wake us up from select(2) in #join when signals
  # are trapped.  See trap_deferred.
  init_self_pipe!

  # setup signal handlers before writing pid file in case people get
  # trigger happy and send signals as soon as the pid file exists.
  # Note that signals don't actually get handled until the #join method
  QUEUE_SIGS.each { |sig| trap(sig) { SIG_QUEUE << sig; awaken_master } }
  trap(:CHLD) { awaken_master }
  self.pid = config[:pid]

  self.master_pid = $$
  build_app! if preload_app
  maintain_worker_count
  self
end
stderr_path=(path) click to toggle source
# File lib/unicorn/http_server.rb, line 195
def stderr_path=(path); redirect_io($stderr, path); end
stdout_path=(path) click to toggle source
# File lib/unicorn/http_server.rb, line 194
def stdout_path=(path); redirect_io($stdout, path); end
stop(graceful = true) click to toggle source

Terminates all workers, but does not exit master process

# File lib/unicorn/http_server.rb, line 344
def stop(graceful = true)
  self.listeners = []
  limit = Time.now + timeout
  until WORKERS.empty? || Time.now > limit
    kill_each_worker(graceful ? :QUIT : :TERM)
    sleep(0.1)
    reap_all_workers
  end
  kill_each_worker(:KILL)
end
trust_x_forwarded() click to toggle source
# File lib/unicorn/http_server.rb, line 372
def trust_x_forwarded
  Unicorn::HttpParser.trust_x_forwarded?
end
trust_x_forwarded=(bool) click to toggle source
# File lib/unicorn/http_server.rb, line 376
def trust_x_forwarded=(bool)
  Unicorn::HttpParser.trust_x_forwarded = bool
end

[Validate]

Generated with the Darkfish Rdoc Generator 2.