mctop 0.0.3 → 0.0.4

Sign up to get free protection for your applications and to get access to all the features.
data/README.md CHANGED
@@ -8,16 +8,18 @@ bandwidth.
8
8
  You can read more detail about why this tool evovled over on our
9
9
  [code as craft](http://codeascraft.etsy.com/2012/12/13/mctop-a-tool-for-analyzing-memcache-get-traffic) blog.
10
10
 
11
- mctop depends on the [ruby-pcap](https://rubygems.org/gems/ruby-pcap) gem, if you don't have
12
- this installed you'll need to ensure you have the development pcap libraries (libpcap-devel
11
+ mctop depends on the [ruby-pcap](https://rubygems.org/gems/ruby-pcap) gem, if you don't have
12
+ this installed you'll need to ensure you have the development pcap libraries (libpcap-devel
13
13
  package on most linux distros) to build the native gem.
14
14
 
15
+ ![](http://etsycodeascraft.files.wordpress.com/2012/12/mctop.jpg)
16
+
15
17
  ## How it works
16
18
 
17
19
  mctop sniffs network traffic collecting memcache `VALUE` responses and calculates from
18
20
  traffic statistics for each key seen. It currently reports on the following metrics per key:
19
21
 
20
- * **calls** - the number of times the key has been called since mctop started
22
+ * **calls** - the number of times the key has been called since mctop started
21
23
  * **objsize** - the size of the object stored for that key
22
24
  * **req/sec** - the number of requests per second for the key
23
25
  * **bw (kbps)** - the estimated netowrk bandwidth consumed by this key in kilobits-per-second
@@ -37,6 +39,7 @@ the quickest way to get it running is to:
37
39
 
38
40
  Usage: mctop [options]
39
41
  -i, --interface=NIC Network interface to sniff (required)
42
+ -p, --port=PORT Network port to sniff on (default 11211)
40
43
  -d, --discard=THRESH Discard keys with request/sec rate below THRESH
41
44
  -r, --refresh=MS Refresh the stats display every MS milliseconds
42
45
  -h, --help Show usage info
@@ -61,8 +64,12 @@ The following details are displayed in the status bar
61
64
  * `packets` - packets received and dropped by libpcap (% is percentage of packets dropped)
62
65
  * `rt` - the time taken to sort and render the stats
63
66
 
67
+ ## Changelog
68
+
69
+ * 2012-12-14 - Now compatible with Ruby 1.8.x (tested on 1.8.7-p371)
70
+
64
71
  ## Known issues / Gotchas
65
72
 
66
73
  ### ruby-pcap drops packets at high volume
67
- from my testing the ruby-pcap native interface to libpcap struggles to keep up with high packet rates (in what we see on a production memcache instance) you can keep an eye on the packets recv/drop and loss percentage on the status bar at the bottom of the UI to get an idea of the packet
74
+ from my testing the ruby-pcap native interface to libpcap struggles to keep up with high packet rates (in what we see on a production memcache instance) you can keep an eye on the packets recv/drop and loss percentage on the status bar at the bottom of the UI to get an idea of the packet
68
75
 
data/bin/mctop CHANGED
@@ -6,6 +6,7 @@
6
6
 
7
7
  $:.unshift File.join(File.dirname(__FILE__),'..','lib')
8
8
 
9
+ require 'rubygems'
9
10
  require 'cmdline'
10
11
  require 'sniffer'
11
12
  require 'ui'
@@ -10,15 +10,21 @@ class CmdLine
10
10
  @config[:nic] = nic
11
11
  end
12
12
 
13
+ opt.on('-p', '--port=PORT', 'Network port to sniff on (default 11211)') do |port|
14
+ @config[:port] = port
15
+ end
16
+
17
+ @config[:discard_thresh] = 0
13
18
  opt.on '-d', '--discard=THRESH', Float, 'Discard keys with request/sec rate below THRESH' do |discard_thresh|
14
19
  @config[:discard_thresh] = discard_thresh
15
20
  end
16
21
 
22
+ @config[:refresh_rate] = 500
17
23
  opt.on '-r', '--refresh=MS', Float, 'Refresh the stats display every MS milliseconds' do |refresh_rate|
18
24
  @config[:refresh_rate] = refresh_rate
19
25
  end
20
26
 
21
- opt.on_tail '-h', '--help', 'Show usage info' do
27
+ opt.on_tail '-h', '--help', 'Show usage info' do
22
28
  puts opts
23
29
  exit
24
30
  end
@@ -1,10 +1,12 @@
1
1
  require 'pcap'
2
+ require 'thread'
2
3
 
3
- class MemcacheSniffer
4
+ class MemcacheSniffer
4
5
  attr_accessor :metrics, :semaphore
5
6
 
6
7
  def initialize(config)
7
8
  @source = config[:nic]
9
+ @port = config.has_key?(:port) ? config[:port] : 11211
8
10
 
9
11
  @metrics = {}
10
12
  @metrics[:calls] = {}
@@ -18,12 +20,12 @@ class MemcacheSniffer
18
20
 
19
21
  def start
20
22
  cap = Pcap::Capture.open_live(@source, 1500)
21
-
23
+
22
24
  @metrics[:start_time] = Time.new.to_f
23
25
 
24
26
  @done = false
25
27
 
26
- cap.setfilter('port 11211')
28
+ cap.setfilter("port #{@port}")
27
29
  cap.loop do |packet|
28
30
  @metrics[:stats] = cap.stats
29
31
 
data/lib/ui.rb CHANGED
@@ -4,12 +4,14 @@ include Curses
4
4
 
5
5
  class UI
6
6
  def initialize(config)
7
+ @config = config
8
+
7
9
  init_screen
8
10
  cbreak
9
11
  curs_set(0)
10
12
 
11
13
  # set keyboard input timeout - sneaky way to manage refresh rate
12
- Curses.timeout = config.has_key?(:refresh_rate) ? config[:refresh_rate] : 500
14
+ Curses.timeout = @config[:refresh_rate]
13
15
 
14
16
  if can_change_color?
15
17
  start_color
@@ -22,9 +24,6 @@ class UI
22
24
  @stat_col_width = 10
23
25
  @key_col_width = 0
24
26
 
25
- # we will delete any keys from the metrics table whose req/sec rate is below discard_rate
26
- @discard_thresh = config.has_key?(:discard_thresh) ? config[:discard_thresh] : 0
27
-
28
27
  @commands = {
29
28
  'Q' => "quit",
30
29
  'C' => "sort by calls",
@@ -63,7 +62,7 @@ class UI
63
62
 
64
63
  # calculate packet loss ratio
65
64
  if sniffer.metrics[:stats][:recv] > 0
66
- loss = (sniffer.metrics[:stats][:drop].to_f / sniffer.metrics[:stats][:recv].to_f) * 100
65
+ loss = sprintf("%5.2f", (sniffer.metrics[:stats][:drop].to_f / sniffer.metrics[:stats][:recv].to_f) * 100)
67
66
  else
68
67
  loss = 0
69
68
  end
@@ -74,7 +73,7 @@ class UI
74
73
  header_summary = sprintf "%-28s %-14s %-30s",
75
74
  "sort mode: #{sort_mode.to_s} (#{sort_order.to_s})",
76
75
  "keys: #{sniffer.metrics[:calls].keys.count}",
77
- "packets (recv/dropped): #{sniffer.metrics[:stats][:recv]} / #{sniffer.metrics[:stats][:drop]} (#{loss.round(2)}%)"
76
+ "packets (recv/dropped): #{sniffer.metrics[:stats][:recv]} / #{sniffer.metrics[:stats][:drop]} (#{loss}%)"
78
77
  addstr(sprintf "%-#{cols}s", header_summary)
79
78
 
80
79
  # reset colours for main key display
@@ -95,7 +94,7 @@ class UI
95
94
  # if req/sec is <= the discard threshold delete those keys from
96
95
  # the metrics hash - this is a hack to manage the size of the
97
96
  # metrics hash in high volume environments
98
- if reqsec <= @discard_thresh
97
+ if reqsec <= @config[:discard_thresh]
99
98
  sniffer.metrics[:calls].delete(k)
100
99
  sniffer.metrics[:objsize].delete(k)
101
100
  sniffer.metrics[:reqsec].delete(k)
@@ -151,7 +150,20 @@ class UI
151
150
  end
152
151
 
153
152
  def input_handler
154
- getch
153
+ # Curses.getch has a bug in 1.8.x causing non-blocking
154
+ # calls to block reimplemented using IO.select
155
+ if RUBY_VERSION =~ /^1.8/
156
+ refresh_secs = @config[:refresh_rate].to_f / 1000
157
+
158
+ if IO.select([STDIN], nil, nil, refresh_secs)
159
+ c = getch
160
+ c.chr
161
+ else
162
+ nil
163
+ end
164
+ else
165
+ getch
166
+ end
155
167
  end
156
168
 
157
169
  def done
@@ -4,7 +4,7 @@ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
4
 
5
5
  Gem::Specification.new do |gem|
6
6
  gem.name = "mctop"
7
- gem.version = "0.0.3"
7
+ gem.version = "0.0.4"
8
8
  gem.authors = ["Marcus Barczak"]
9
9
  gem.email = ["marcus@etsy.com"]
10
10
  gem.description = %q{mctop - a realtime memcache key analyzer}
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: mctop
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.3
4
+ version: 0.0.4
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2012-12-13 00:00:00.000000000 Z
12
+ date: 2012-12-14 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: ruby-pcap