lib/raindrops/middleware.rb in raindrops-0.4.1 vs lib/raindrops/middleware.rb in raindrops-0.5.0
- old
+ new
@@ -1,79 +1,150 @@
# -*- encoding: binary -*-
require 'raindrops'
-# Raindrops middleware should be loaded at the top of Rack
-# middleware stack before other middlewares for maximum accuracy.
-class Raindrops
-class Middleware < ::Struct.new(:app, :stats, :path, :tcp, :unix)
+# Raindrops::Middleware is Rack middleware that allows snapshotting
+# current activity from an HTTP request. For all operating systems,
+# it returns at least the following fields:
+#
+# * calling - the number of application dispatchers on your machine
+# * writing - the number of clients being written to on your machine
+#
+# Additional fields are available for \Linux users.
+#
+# It should be loaded at the top of Rack middleware stack before other
+# middlewares for maximum accuracy.
+#
+# === Usage (Rainbows!/Unicorn preload_app=false)
+#
+# If you're using preload_app=false (the default) in your Rainbows!/Unicorn
+# config file, you'll need to create the global Stats object before
+# forking.
+#
+# require 'raindrops'
+# $stats ||= Raindrops::Middleware::Stats.new
+#
+# In your Rack config.ru:
+#
+# use Raindrops::Middleware, :stats => $stats
+#
+# === Usage (Rainbows!/Unicorn preload_app=true)
+#
+# If you're using preload_app=true in your Rainbows!/Unicorn
+# config file, just add the middleware to your stack:
+#
+# In your Rack config.ru:
+#
+# use Raindrops::Middleware
+#
+# === Linux-only extras!
+#
+# To get bound listener statistics under \Linux, you need to specify the
+# listener names for your server. You can even include listen sockets for
+# *other* servers on the same machine. This can be handy for monitoring
+# your nginx proxy as well.
+#
+# In your Rack config.ru, just pass the :listeners argument as an array of
+# strings (along with any other arguments). You can specify any
+# combination of TCP or Unix domain socket names:
+#
+# use Raindrops::Middleware, :listeners => %w(0.0.0.0:80 /tmp/.sock)
+#
+# If you're running Unicorn 0.98.0 or later, you don't have to pass in
+# the :listeners array, Raindrops will automatically detect the listeners
+# used by Unicorn master process. This does not detect listeners in
+# different processes, of course.
+#
+# The response body includes the following stats for each listener
+# (see also Raindrops::ListenStats):
+#
+# * active - total number of active clients on that listener
+# * queued - total number of queued (pre-accept()) clients on that listener
+#
+# = Demo Server
+#
+# There is a server running this middleware (and Watcher) at
+# http://raindrops-demo.bogomips.org/_raindrops
+#
+# Also check out the Watcher demo at http://raindrops-demo.bogomips.org/
+#
+# The demo server is only limited to 30 users, so be sure not to abuse it
+# by using the /tail/ endpoint too much.
+#
+class Raindrops::Middleware
+ attr_accessor :app, :stats, :path, :tcp, :unix # :nodoc:
+ # A Raindrops::Struct used to count the number of :calling and :writing
+ # clients. This struct is intended to be shared across multiple processes
+ # and both counters are updated atomically.
+ #
+ # This is supported on all operating systems supported by Raindrops
+ class Stats < Raindrops::Struct.new(:calling, :writing)
+ end
+
# :stopdoc:
- Stats = Raindrops::Struct.new(:calling, :writing)
PATH_INFO = "PATH_INFO"
+ require "raindrops/middleware/proxy"
# :startdoc:
+ # +app+ may be any Rack application, this middleware wraps it.
+ # +opts+ is a hash that understands the following members:
+ #
+ # * :stats - Raindrops::Middleware::Stats struct (default: Stats.new)
+ # * :path - HTTP endpoint used for reading the stats (default: "/_raindrops")
+ # * :listeners - array of host:port or socket paths (default: from Unicorn)
def initialize(app, opts = {})
- super(app, opts[:stats] || Stats.new, opts[:path] || "/_raindrops")
+ @app = app
+ @stats = opts[:stats] || Stats.new
+ @path = opts[:path] || "/_raindrops"
tmp = opts[:listeners]
if tmp.nil? && defined?(Unicorn) && Unicorn.respond_to?(:listener_names)
tmp = Unicorn.listener_names
end
+ @tcp = @unix = nil
if tmp
- self.tcp = tmp.grep(/\A[^:]+:\d+\z/)
- self.unix = tmp.grep(%r{\A/})
- self.tcp = nil if tcp.empty?
- self.unix = nil if unix.empty?
+ @tcp = tmp.grep(/\A.+:\d+\z/)
+ @unix = tmp.grep(%r{\A/})
+ @tcp = nil if @tcp.empty?
+ @unix = nil if @unix.empty?
end
end
# standard Rack endpoint
- def call(env)
- env[PATH_INFO] == path ? stats_response : dup._call(env)
- end
+ def call(env) # :nodoc:
+ env[PATH_INFO] == @path and return stats_response
+ begin
+ @stats.incr_calling
- def _call(env)
- stats.incr_calling
- status, headers, self.app = app.call(env)
+ status, headers, body = @app.call(env)
+ rv = [ status, headers, Proxy.new(body, @stats) ]
- # the Rack server will start writing headers soon after this method
- stats.incr_writing
- [ status, headers, self ]
+ # the Rack server will start writing headers soon after this method
+ @stats.incr_writing
+ rv
ensure
- stats.decr_calling
+ @stats.decr_calling
+ end
end
- # yield to the Rack server here for writing
- def each(&block)
- app.each(&block)
- end
+ def stats_response # :nodoc:
+ body = "calling: #{@stats.calling}\n" \
+ "writing: #{@stats.writing}\n"
- # the Rack server should call this after #each (usually ensure-d)
- def close
- stats.decr_writing
- app.close if app.respond_to?(:close)
- end
-
- def stats_response
- body = "calling: #{stats.calling}\n" \
- "writing: #{stats.writing}\n"
-
- if defined?(Linux)
- Linux.tcp_listener_stats(tcp).each do |addr,stats|
+ if defined?(Raindrops::Linux)
+ Raindrops::Linux.tcp_listener_stats(@tcp).each do |addr,stats|
body << "#{addr} active: #{stats.active}\n" \
"#{addr} queued: #{stats.queued}\n"
- end if tcp
- Linux.unix_listener_stats(unix).each do |addr,stats|
+ end if @tcp
+ Raindrops::Linux.unix_listener_stats(@unix).each do |addr,stats|
body << "#{addr} active: #{stats.active}\n" \
"#{addr} queued: #{stats.queued}\n"
- end if unix
+ end if @unix
end
headers = {
"Content-Type" => "text/plain",
"Content-Length" => body.size.to_s,
}
[ 200, headers, [ body ] ]
end
-
-end
end