From f65783b5a107fb515b46b17998b276d0fafd4be1 Mon Sep 17 00:00:00 2001 From: Eric Wong Date: Sun, 29 Mar 2009 17:38:01 -0700 Subject: configurator: per-listener backlog, {rcv,snd}buf config Instead of having global options for all listeners, make all socket options per-listener. This allows reverse-proxies to pick different listeners to get different options on different sockets. Given a cluster of machines (10.0.0.1, 10.0.0.2, 10.0.0.3) running Unicorn with the following config: ------------------ 8< ---------------- listen "/tmp/local.sock", :backlog => 1 listen "*:8080" # use the backlog=1024 default ------------------ 8< ---------------- It is possible to configure a reverse proxy to try to use "/tmp/local.sock" first and then fall back to using the TCP listener on port 8080 in a failover configuration. Thus the nginx upstream configuration on 10.0.0.1 to compliment this would be: ------------------ 8< ---------------- upstream unicorn_cluster { # reject connections ASAP if we are overloaded server unix:/tmp/local.sock; # fall back to other machines in the cluster via "backup" # listeners which have a large backlog queue. server 10.0.0.2:8080 backup; server 10.0.0.3:8080 backup; } ------------------ 8< ---------------- This removes the global "backlog" config option which was inflexible with multiple machines in a cluster and exposes the ability to change SO_SNDBUF/SO_RCVBUF via setsockopt(2) for the first time. --- lib/unicorn.rb | 3 +- lib/unicorn/configurator.rb | 71 +++++++++++++++++++++++++++++++-------------- 2 files changed, 52 insertions(+), 22 deletions(-) (limited to 'lib') diff --git a/lib/unicorn.rb b/lib/unicorn.rb index 34189ef..d56f251 100644 --- a/lib/unicorn.rb +++ b/lib/unicorn.rb @@ -59,6 +59,7 @@ module Unicorn @request = @rd_sig = @wr_sig = nil @reexec_pid = 0 @config = Configurator.new(options.merge(:use_defaults => true)) + @listener_opts = {} @config.commit!(self, :skip => [:listeners, :pid]) @listeners = [] end @@ -135,7 +136,7 @@ module Unicorn def listen(address) return if String === address && listener_names.include?(address) - if io = bind_listen(address, { :backlog => @backlog }) + if io = bind_listen(address, @listener_opts[address] || {}) if Socket == io.class @io_purgatory << io io = server_cast(io) diff --git a/lib/unicorn/configurator.rb b/lib/unicorn/configurator.rb index b4713c5..4a085a1 100644 --- a/lib/unicorn/configurator.rb +++ b/lib/unicorn/configurator.rb @@ -43,7 +43,6 @@ module Unicorn server.logger.info("forked child re-executing...") }, :pid => nil, - :backlog => 1024, :preload_app => false, :stderr_path => nil, :stdout_path => nil, @@ -83,23 +82,6 @@ module Unicorn @set[key] end - # Changes the listen() syscall backlog to +nr+ for yet-to-be-created - # sockets. Due to limitations of the OS, this cannot affect - # existing listener sockets in any way, sockets must be completely - # closed and rebound (inherited sockets preserve their existing - # backlog setting). Some operating systems allow negative values - # here to specify the maximum allowable value. See the listen(2) - # syscall documentation of your OS for the exact semantics of this. - # - # If you are running unicorn on multiple machines, lowering this number - # can help your load balancer detect when a machine is overloaded - # and give requests to a different machine. - def backlog(nr) - Integer === nr or raise ArgumentError, - "not an integer: backlog=#{nr.inspect}" - @set[:backlog] = nr - end - # sets object to the +new+ Logger-like object. The new logger-like # object must respond to the following methods: # +debug+, +info+, +warn+, +error+, +fatal+, +close+ @@ -177,10 +159,57 @@ module Unicorn @set[:listeners] = addresses end - # adds an +address+ to the existing listener set - def listen(address) + # adds an +address+ to the existing listener set. + # + # The following options may be specified (but are generally not needed): + # + # +backlog+: this is the backlog of the listen() syscall. + # + # Some operating systems allow negative values here to specify the + # maximum allowable value. In most cases, this number is only + # recommendation and there are other OS-specific tunables and + # variables that can affect this number. See the listen(2) + # syscall documentation of your OS for the exact semantics of + # this. + # + # If you are running unicorn on multiple machines, lowering this number + # can help your load balancer detect when a machine is overloaded + # and give requests to a different machine. + # + # Default: 1024 + # + # +rcvbuf+, +sndbuf+: maximum send and receive buffer sizes of sockets + # + # These correspond to the SO_RCVBUF and SO_SNDBUF settings which + # can be set via the setsockopt(2) syscall. Some kernels + # (e.g. Linux 2.4+) have intelligent auto-tuning mechanisms and + # there is no need (and it is sometimes detrimental) to specify them. + # + # See the socket API documentation of your operating system + # to determine the exact semantics of these settings and + # other operating system-specific knobs where they can be + # specified. + # + # Defaults: operating system defaults + # + # Due to limitations of the operating system, options specified here + # cannot affect existing listener sockets in any way, sockets must be + # completely closed and rebound. + def listen(address, opt = { :backlog => 1024 }) + address = expand_addr(address) + if String === address + Hash === @set[:listener_opts] or + @set[:listener_opts] = Hash.new { |hash,key| hash[key] = {} } + [ :backlog, :sndbuf, :rcvbuf ].each do |key| + value = opt[key] or next + Integer === value or + raise ArgumentError, "not an integer: #{key}=#{value.inspect}" + end + @set[:listener_opts][address].merge!(opt) + end + @set[:listeners] = [] unless Array === @set[:listeners] - @set[:listeners] << expand_addr(address) + @set[:listeners] << address end # sets the +path+ for the PID file of the unicorn master process -- cgit v1.2.3-24-ge0c7