diff options
Diffstat (limited to 'lib')
-rw-r--r-- | lib/raindrops.rb | 27 | ||||
-rw-r--r-- | lib/raindrops/aggregate.rb | 1 | ||||
-rw-r--r-- | lib/raindrops/aggregate/last_data_recv.rb | 24 | ||||
-rw-r--r-- | lib/raindrops/aggregate/pmq.rb | 13 | ||||
-rw-r--r-- | lib/raindrops/last_data_recv.rb | 1 | ||||
-rw-r--r-- | lib/raindrops/linux.rb | 10 | ||||
-rw-r--r-- | lib/raindrops/middleware.rb | 18 | ||||
-rw-r--r-- | lib/raindrops/middleware/proxy.rb | 1 | ||||
-rw-r--r-- | lib/raindrops/struct.rb | 1 | ||||
-rw-r--r-- | lib/raindrops/watcher.rb | 15 |
10 files changed, 80 insertions, 31 deletions
diff --git a/lib/raindrops.rb b/lib/raindrops.rb index d3913a8..c071d57 100644 --- a/lib/raindrops.rb +++ b/lib/raindrops.rb @@ -1,4 +1,5 @@ # -*- encoding: binary -*- +# frozen_string_literal: false # # Each Raindrops object is a container that holds several counters. # It is internally a page-aligned, shared memory area that allows @@ -12,7 +13,7 @@ # Unlike many classes in this package, the core Raindrops class is # intended to be portable to all reasonably modern *nix systems # supporting mmap(). Please let us know if you have portability -# issues, patches or pull requests at mailto:raindrops-public@bogomips.org +# issues, patches or pull requests at mailto:raindrops-public@yhbt.net class Raindrops # Used to represent the number of +active+ and +queued+ sockets for @@ -36,6 +37,30 @@ class Raindrops def total active + queued end + end unless defined? ListenStats + + # call-seq: + # Raindrops.new(size, io: nil) -> raindrops object + # + # Initializes a Raindrops object to hold +size+ counters. +size+ is + # only a hint and the actual number of counters the object has is + # dependent on the CPU model, number of cores, and page size of + # the machine. The actual size of the object will always be equal + # or greater than the specified +size+. + # If +io+ is provided, then the Raindrops memory will be backed by + # the specified file; otherwise, it will allocate anonymous memory. + # The IO object must respond to +truncate+, as this is used to set + # the size of the file. + # If +zero+ is provided, then the memory region is zeroed prior to + # returning. This is only meaningful if +io+ is also provided; in + # that case it controls whether any existing counter values in +io+ + # are retained (false) or whether it is entirely zeroed (true). + def initialize(size, io: nil, zero: false) + # This ruby wrapper exists to handle the keyword-argument handling, + # which is otherwise kind of awkward in C. We delegate the keyword + # arguments to the _actual_ initialize implementation as positional + # args. + initialize_cimpl(size, io, zero) end autoload :Linux, 'raindrops/linux' diff --git a/lib/raindrops/aggregate.rb b/lib/raindrops/aggregate.rb index 4fb731f..9ed7eb7 100644 --- a/lib/raindrops/aggregate.rb +++ b/lib/raindrops/aggregate.rb @@ -1,4 +1,5 @@ # -*- encoding: binary -*- +# frozen_string_literal: false # # raindrops may use the {aggregate}[https://github.com/josephruscio/aggregate] # RubyGem to aggregate statistics from TCP_Info lookups. diff --git a/lib/raindrops/aggregate/last_data_recv.rb b/lib/raindrops/aggregate/last_data_recv.rb index 6919fbc..2205208 100644 --- a/lib/raindrops/aggregate/last_data_recv.rb +++ b/lib/raindrops/aggregate/last_data_recv.rb @@ -1,4 +1,5 @@ # -*- encoding: binary -*- +# frozen_string_literal: false require "socket" # # @@ -10,6 +11,8 @@ require "socket" # Methods wrapped include: # - TCPServer#accept # - TCPServer#accept_nonblock +# - Socket#accept +# - Socket#accept_nonblock # - Kgio::TCPServer#kgio_accept # - Kgio::TCPServer#kgio_tryaccept module Raindrops::Aggregate::LastDataRecv @@ -33,8 +36,10 @@ module Raindrops::Aggregate::LastDataRecv # automatically extends any TCPServer objects used by Unicorn def self.cornify! - Unicorn::HttpServer::LISTENERS.each do |sock| - sock.extend(self) if TCPServer === sock + Unicorn::HttpServer::LISTENERS.each do |s| + if TCPServer === s || (s.instance_of?(Socket) && s.local_address.ip?) + s.extend(self) + end end end @@ -60,8 +65,8 @@ module Raindrops::Aggregate::LastDataRecv count! super end - def accept_nonblock - count! super + def accept_nonblock(exception: true) + count! super(exception: exception) end # :startdoc: @@ -72,12 +77,19 @@ module Raindrops::Aggregate::LastDataRecv # # We require TCP_DEFER_ACCEPT on the listen socket for # +last_data_recv+ to be accurate - def count!(io) + def count!(ret) + case ret + when :wait_readable + when Array # Socket#accept_nonblock + io = ret[0] + else # TCPSocket#accept_nonblock + io = ret + end if io x = Raindrops::TCP_Info.new(io) @raindrops_aggregate << x.last_data_recv end - io + ret end end diff --git a/lib/raindrops/aggregate/pmq.rb b/lib/raindrops/aggregate/pmq.rb index 8623cb1..94bdf4f 100644 --- a/lib/raindrops/aggregate/pmq.rb +++ b/lib/raindrops/aggregate/pmq.rb @@ -1,4 +1,5 @@ # -*- encoding: binary -*- +# frozen_string_literal: false require "tempfile" require "aggregate" require "posix_mq" @@ -142,8 +143,8 @@ class Raindrops::Aggregate::PMQ warn "Unhandled exception in #{__FILE__}:#{__LINE__}: #{e}" break end while true - ensure - flush_master + ensure + flush_master end # Loads the last shared \Aggregate from the master thread/process @@ -175,14 +176,14 @@ class Raindrops::Aggregate::PMQ # worker thread or process def stop_master_loop sleep 0.1 until mq_send(false) - rescue Errno::EINTR - retry + rescue Errno::EINTR + retry end def lock! io, type # :nodoc: io.fcntl Fcntl::F_SETLKW, type - rescue Errno::EINTR - retry + rescue Errno::EINTR + retry end # we use both a mutex for thread-safety and fcntl lock for process-safety diff --git a/lib/raindrops/last_data_recv.rb b/lib/raindrops/last_data_recv.rb index b4808a1..e6c47e1 100644 --- a/lib/raindrops/last_data_recv.rb +++ b/lib/raindrops/last_data_recv.rb @@ -1,4 +1,5 @@ # -*- encoding: binary -*- +# frozen_string_literal: false require "raindrops" # This is highly experimental! diff --git a/lib/raindrops/linux.rb b/lib/raindrops/linux.rb index 4166ec7..a76192c 100644 --- a/lib/raindrops/linux.rb +++ b/lib/raindrops/linux.rb @@ -1,4 +1,5 @@ # -*- encoding: binary -*- +# frozen_string_literal: false # For reporting TCP ListenStats, users of older \Linux kernels need to ensure # that the the "inet_diag" and "tcp_diag" kernel modules are loaded as they do @@ -14,8 +15,7 @@ module Raindrops::Linux # The standard proc path for active UNIX domain sockets, feel free to call # String#replace on this if your /proc is mounted in a non-standard location # for whatever reason - PROC_NET_UNIX_ARGS = %w(/proc/net/unix) - defined?(::Encoding) and PROC_NET_UNIX_ARGS.push({ :encoding => "binary" }) + PROC_NET_UNIX_ARGS = [ '/proc/net/unix', { encoding: "binary" }] # Get ListenStats from an array of +paths+ # @@ -42,11 +42,11 @@ module Raindrops::Linux else paths = paths.map do |path| path = path.dup - path.force_encoding(Encoding::BINARY) if defined?(Encoding) + path.force_encoding(Encoding::BINARY) if File.symlink?(path) link = path path = File.readlink(link) - path.force_encoding(Encoding::BINARY) if defined?(Encoding) + path.force_encoding(Encoding::BINARY) rv[link] = rv[path] # vivify ListenerStats else rv[path] # vivify ListenerStats @@ -57,7 +57,7 @@ module Raindrops::Linux paths = /^\w+: \d+ \d+ (\d+) \d+ (\d+)\s+\d+ (#{paths.join('|')})$/n # no point in pread since we can't stat for size on this file - File.read(*PROC_NET_UNIX_ARGS).scan(paths) do |s| + File.read(PROC_NET_UNIX_ARGS[0], encoding: 'binary').scan(paths) do |s| path = s[-1] case s[0] when "00000000" # client sockets diff --git a/lib/raindrops/middleware.rb b/lib/raindrops/middleware.rb index ea7f08a..25b5a1e 100644 --- a/lib/raindrops/middleware.rb +++ b/lib/raindrops/middleware.rb @@ -1,5 +1,7 @@ # -*- encoding: binary -*- +# frozen_string_literal: false require 'raindrops' +require 'thread' # Raindrops::Middleware is Rack middleware that allows snapshotting # current activity from an HTTP request. For all operating systems, @@ -62,9 +64,9 @@ require 'raindrops' # = Demo Server # # There is a server running this middleware (and Watcher) at -# https://raindrops-demo.bogomips.org/_raindrops +# https://yhbt.net/raindrops-demo/_raindrops # -# Also check out the Watcher demo at https://raindrops-demo.bogomips.org/ +# Also check out the Watcher demo at https://yhbt.net/raindrops-demo/ # # The demo server is only limited to 30 users, so be sure not to abuse it # by using the /tail/ endpoint too much. @@ -93,11 +95,12 @@ class Raindrops::Middleware @app = app @stats = opts[:stats] || Stats.new @path = opts[:path] || "/_raindrops" + @mtx = Mutex.new tmp = opts[:listeners] if tmp.nil? && defined?(Unicorn) && Unicorn.respond_to?(:listener_names) tmp = Unicorn.listener_names end - @tcp = @unix = nil + @nl_sock = @tcp = @unix = nil if tmp @tcp = tmp.grep(/\A.+:\d+\z/) @@ -129,9 +132,12 @@ class Raindrops::Middleware "writing: #{@stats.writing}\n" if defined?(Raindrops::Linux.tcp_listener_stats) - Raindrops::Linux.tcp_listener_stats(@tcp).each do |addr,stats| - body << "#{addr} active: #{stats.active}\n" \ - "#{addr} queued: #{stats.queued}\n" + @mtx.synchronize do + @nl_sock ||= Raindrops::InetDiagSocket.new + Raindrops::Linux.tcp_listener_stats(@tcp, @nl_sock).each do |addr,stats| + body << "#{addr} active: #{stats.active}\n" \ + "#{addr} queued: #{stats.queued}\n" + end end if @tcp Raindrops::Linux.unix_listener_stats(@unix).each do |addr,stats| body << "#{addr} active: #{stats.active}\n" \ diff --git a/lib/raindrops/middleware/proxy.rb b/lib/raindrops/middleware/proxy.rb index a7c8e66..433950c 100644 --- a/lib/raindrops/middleware/proxy.rb +++ b/lib/raindrops/middleware/proxy.rb @@ -1,4 +1,5 @@ # -*- encoding: binary -*- +# frozen_string_literal: false # :stopdoc: # This class is used by Raindrops::Middleware to proxy application # response bodies. There should be no need to use it directly. diff --git a/lib/raindrops/struct.rb b/lib/raindrops/struct.rb index e81a78e..7233ce8 100644 --- a/lib/raindrops/struct.rb +++ b/lib/raindrops/struct.rb @@ -1,4 +1,5 @@ # -*- encoding: binary -*- +# frozen_string_literal: false # This is a wrapper around Raindrops objects much like the core Ruby # \Struct can be seen as a wrapper around the core \Array class. diff --git a/lib/raindrops/watcher.rb b/lib/raindrops/watcher.rb index b0615f7..8fc0772 100644 --- a/lib/raindrops/watcher.rb +++ b/lib/raindrops/watcher.rb @@ -1,4 +1,5 @@ # -*- encoding: binary -*- +# frozen_string_literal: false require "thread" require "time" require "socket" @@ -35,28 +36,28 @@ require "aggregate" # Returns a plain text summary + histogram with X-* HTTP headers for # active connections. # -# e.g.: curl https://raindrops-demo.bogomips.org/active/0.0.0.0%3A80.txt +# e.g.: curl https://yhbt.net/raindrops-demo/active/0.0.0.0%3A80.txt # # === GET /active/$LISTENER.html # # Returns an HTML summary + histogram with X-* HTTP headers for # active connections. # -# e.g.: curl https://raindrops-demo.bogomips.org/active/0.0.0.0%3A80.html +# e.g.: curl https://yhbt.net/raindrops-demo/active/0.0.0.0%3A80.html # # === GET /queued/$LISTENER.txt # # Returns a plain text summary + histogram with X-* HTTP headers for # queued connections. # -# e.g.: curl https://raindrops-demo.bogomips.org/queued/0.0.0.0%3A80.txt +# e.g.: curl https://yhbt.net/raindrops-demo/queued/0.0.0.0%3A80.txt # # === GET /queued/$LISTENER.html # # Returns an HTML summary + histogram with X-* HTTP headers for # queued connections. # -# e.g.: curl https://raindrops-demo.bogomips.org/queued/0.0.0.0%3A80.html +# e.g.: curl https://yhbt.net/raindrops-demo/queued/0.0.0.0%3A80.html # # === POST /reset/$LISTENER # @@ -95,9 +96,9 @@ require "aggregate" # # = Demo Server # -# There is a server running this app at https://raindrops-demo.bogomips.org/ +# There is a server running this app at https://yhbt.net/raindrops-demo/ # The Raindrops::Middleware demo is also accessible at -# https://raindrops-demo.bogomips.org/_raindrops +# https://yhbt.net/raindrops-demo/_raindrops # # The demo server is only limited to 30 users, so be sure not to abuse it # by using the /tail/ endpoint too much. @@ -106,7 +107,7 @@ class Raindrops::Watcher attr_reader :snapshot include Rack::Utils include Raindrops::Linux - DOC_URL = "https://bogomips.org/raindrops/Raindrops/Watcher.html" + DOC_URL = "https://yhbt.net/raindrops/Raindrops/Watcher.html" Peak = Struct.new(:first, :last) def initialize(opts = {}) |