rainbows.git  about / heads / tags
Unicorn for sleepy apps and slow clients
blob d0b3e8a92e1781b91bd17cd135bc485a2c8c11f1 4168 bytes (raw)
$ git show v0.95.1:lib/rainbows.rb	# shows this blob on the CLI

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
 
# -*- encoding: binary -*-
require 'unicorn'
# the value passed to TCP_DEFER_ACCEPT actually matters in Linux 2.6.32+
Unicorn::SocketHelper::DEFAULTS[:tcp_defer_accept] = 60

require 'rainbows/error'
require 'rainbows/configurator'
require 'fcntl'

module Rainbows

  # global vars because class/instance variables are confusing me :<
  # this struct is only accessed inside workers and thus private to each
  # G.cur may not be used in the network concurrency model
  # :stopdoc:
  class State < Struct.new(:alive,:m,:cur,:kato,:server,:tmp,:expire)
    def tick
      tmp.chmod(self.m = m == 0 ? 1 : 0)
      exit!(2) if expire && Time.now >= expire
      alive && server.master_pid == Process.ppid or quit!
    end

    def quit!
      self.alive = false
      self.expire ||= Time.now + (server.timeout * 2.0)
      server.class.const_get(:LISTENERS).map! { |s| s.close rescue nil }
      false
    end
  end
  G = State.new(true, 0, 0, 5)
  O = {}
  # :startdoc:

  require 'rainbows/const'
  require 'rainbows/http_server'
  require 'rainbows/response'
  require 'rainbows/base'
  require 'rainbows/tee_input'
  autoload :Sendfile, 'rainbows/sendfile'
  autoload :AppPool, 'rainbows/app_pool'
  autoload :DevFdResponse, 'rainbows/dev_fd_response'
  autoload :MaxBody, 'rainbows/max_body'
  autoload :QueuePool, 'rainbows/queue_pool'

  class << self

    # Sleeps the current application dispatch.  This will pick the
    # optimal method to sleep depending on the concurrency model chosen
    # (which may still suck and block the entire process).  Using this
    # with the basic :Rev or :EventMachine models is not recommended.
    # This should be used within your Rack application.
    def sleep(nr)
      case G.server.use
      when :FiberPool, :FiberSpawn
        Rainbows::Fiber.sleep(nr)
      when :RevFiberSpawn
        Rainbows::Fiber::Rev::Sleeper.new(nr)
      when :Revactor
        Actor.sleep(nr)
      else
        Kernel.sleep(nr)
      end
    end

    # runs the Rainbows! HttpServer with +app+ and +options+ and does
    # not return until the server has exited.
    def run(app, options = {}) # :nodoc:
      HttpServer.new(app, options).start.join
    end

    # returns nil if accept fails
    def sync_accept(sock) # :nodoc:
      rv = sock.accept
      rv.fcntl(Fcntl::F_SETFD, Fcntl::FD_CLOEXEC)
      rv
    rescue Errno::EAGAIN, Errno::ECONNABORTED, Errno::EINTR
    end

    # returns nil if accept fails
    def accept(sock) # :nodoc:
      rv = sock.accept_nonblock
      rv.fcntl(Fcntl::F_SETFD, Fcntl::FD_CLOEXEC)
      rv
    rescue Errno::EAGAIN, Errno::ECONNABORTED
    end

    # returns a string representing the address of the given client +io+
    # For local UNIX domain sockets, this will return a string referred
    # to by the (non-frozen) Unicorn::HttpRequest::LOCALHOST constant.
    def addr(io) # :nodoc:
      io.respond_to?(:peeraddr) ?
                        io.peeraddr[-1] : Unicorn::HttpRequest::LOCALHOST
    end

    # :stopdoc:
    # the default max body size is 1 megabyte (1024 * 1024 bytes)
    @@max_bytes = 1024 * 1024

    def max_bytes; @@max_bytes; end
    def max_bytes=(nr); @@max_bytes = nr; end
    # :startdoc:
  end

  # :stopdoc:
  # maps models to default worker counts, default worker count numbers are
  # pretty arbitrary and tuning them to your application and hardware is
  # highly recommended
  MODEL_WORKER_CONNECTIONS = {
    :Base => 1, # this one can't change
    :WriterThreadPool => 20,
    :WriterThreadSpawn => 20,
    :Revactor => 50,
    :ThreadSpawn => 30,
    :ThreadPool => 20,
    :Rev => 50,
    :RevThreadSpawn => 50,
    :RevThreadPool => 50,
    :EventMachine => 50,
    :FiberSpawn => 50,
    :FiberPool => 50,
    :ActorSpawn => 50,
    :NeverBlock => 50,
    :RevFiberSpawn => 50,
  }.each do |model, _|
    u = model.to_s.gsub(/([a-z0-9])([A-Z0-9])/) { "#{$1}_#{$2.downcase!}" }
    autoload model, "rainbows/#{u.downcase!}"
  end
  # :startdoc:
  autoload :Fiber, 'rainbows/fiber' # core class
  autoload :ByteSlice, 'rainbows/byte_slice'
  autoload :StreamFile, 'rainbows/stream_file'
  autoload :HttpResponse, 'rainbows/http_response' # deprecated
end

git clone https://yhbt.net/rainbows.git