benchy 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/.gitignore +4 -0
- data/Gemfile +4 -0
- data/Rakefile +1 -0
- data/benchy.gemspec +25 -0
- data/bin/benchy +7 -0
- data/lib/benchy.rb +125 -0
- data/lib/benchy/version.rb +5 -0
- metadata +76 -0
data/.gitignore
ADDED
data/Gemfile
ADDED
data/Rakefile
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require "bundler/gem_tasks"
|
data/benchy.gemspec
ADDED
@@ -0,0 +1,25 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
$:.push File.expand_path("../lib", __FILE__)
|
3
|
+
require "benchy/version"
|
4
|
+
|
5
|
+
Gem::Specification.new do |s|
|
6
|
+
s.name = "benchy"
|
7
|
+
s.version = Benchy::VERSION
|
8
|
+
s.authors = ["Brad Gessler"]
|
9
|
+
s.email = ["brad@bradgessler.com"]
|
10
|
+
s.homepage = ""
|
11
|
+
s.summary = %q{Benchmark HTTP applications}
|
12
|
+
s.description = %q{A dirty-simple HTTP benchmarking application}
|
13
|
+
|
14
|
+
s.rubyforge_project = "benchy"
|
15
|
+
|
16
|
+
s.files = `git ls-files`.split("\n")
|
17
|
+
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
18
|
+
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
19
|
+
s.require_paths = ["lib"]
|
20
|
+
|
21
|
+
# specify any dependencies here; for example:
|
22
|
+
# s.add_development_dependency "rspec"
|
23
|
+
s.add_runtime_dependency "em-http-request"
|
24
|
+
s.add_runtime_dependency "thor"
|
25
|
+
end
|
data/bin/benchy
ADDED
data/lib/benchy.rb
ADDED
@@ -0,0 +1,125 @@
|
|
1
|
+
require "benchy/version"
|
2
|
+
require 'logger'
|
3
|
+
require "thor"
|
4
|
+
|
5
|
+
module Benchy
|
6
|
+
def self.logger
|
7
|
+
@logger ||= Logger.new($stdout)
|
8
|
+
end
|
9
|
+
|
10
|
+
class Dispatcher
|
11
|
+
attr_accessor :concurrency, :request
|
12
|
+
|
13
|
+
def initialize(request, concurrency=1)
|
14
|
+
@request, @concurrency = request, concurrency
|
15
|
+
end
|
16
|
+
|
17
|
+
def run
|
18
|
+
workers.each(&:run)
|
19
|
+
end
|
20
|
+
|
21
|
+
def halt
|
22
|
+
workers.each(&:halt)
|
23
|
+
end
|
24
|
+
|
25
|
+
def workers
|
26
|
+
@workers ||= (0...concurrency).map{|n| Worker.new(request, "worker.#{n}") }
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
class Worker
|
31
|
+
attr_accessor :request, :name
|
32
|
+
|
33
|
+
def initialize(request, name)
|
34
|
+
@request, @name = request, name
|
35
|
+
end
|
36
|
+
|
37
|
+
# Run, and keep running!
|
38
|
+
def run
|
39
|
+
return if halted?
|
40
|
+
|
41
|
+
http = request.em
|
42
|
+
http.callback {
|
43
|
+
Benchy.logger.info "#{name}\t| #{request.method.upcase} #{request.url} - HTTP #{http.response_header.status}"
|
44
|
+
run
|
45
|
+
}
|
46
|
+
http.errback {
|
47
|
+
Benchy.logger.debug "Connection error!"
|
48
|
+
halt # TODO - Make this fail the ping and try again, not halt
|
49
|
+
}
|
50
|
+
end
|
51
|
+
|
52
|
+
def halt
|
53
|
+
@halted = false
|
54
|
+
end
|
55
|
+
|
56
|
+
def halted?
|
57
|
+
!!@halted
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
# Represents an HTTP Request, but can't actually be executed
|
62
|
+
class Request
|
63
|
+
attr_accessor :url, :method, :headers, :body
|
64
|
+
|
65
|
+
def initialize(url, method, headers, body=nil)
|
66
|
+
@url, @method, @headers, @body = url, method, (headers || {}), body
|
67
|
+
end
|
68
|
+
|
69
|
+
# Grab an instance of an Em::Http request so we can run it somewhere.
|
70
|
+
def em
|
71
|
+
EventMachine::HttpRequest.new(url).send(method.downcase,
|
72
|
+
:head => default_headers.merge(headers),
|
73
|
+
:body => body,
|
74
|
+
:connect_timeout => 9000, # Disable
|
75
|
+
:inactivity_timeout => 9000 # Disable
|
76
|
+
)
|
77
|
+
end
|
78
|
+
|
79
|
+
# Setup smart default headers to minimize the chances that a request gets rejected.
|
80
|
+
def default_headers
|
81
|
+
default_headers = {}
|
82
|
+
default_headers['Content-Type'] = 'application/binary-octet' if body
|
83
|
+
default_headers['Accepts'] = '*/*'
|
84
|
+
default_headers
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
# Parse out some command line goodness
|
89
|
+
class CLI < Thor
|
90
|
+
desc "benchmark URL", "Run benchmarks against server"
|
91
|
+
method_option :body,
|
92
|
+
:type => :string,
|
93
|
+
:aliases => '-b',
|
94
|
+
:desc => "Request body"
|
95
|
+
method_option :file,
|
96
|
+
:type => :string,
|
97
|
+
:aliases => '-f',
|
98
|
+
:desc => "File for request body"
|
99
|
+
method_option :headers,
|
100
|
+
:type => :hash,
|
101
|
+
:aliases => '-h',
|
102
|
+
:desc => 'HTTP headers'
|
103
|
+
method_option :method,
|
104
|
+
:type => :string,
|
105
|
+
:desc => "Request method",
|
106
|
+
:aliases => '-m',
|
107
|
+
:default => 'GET'
|
108
|
+
method_option :concurrency,
|
109
|
+
:type => :numeric,
|
110
|
+
:desc => "Concurrent requests",
|
111
|
+
:aliases => '-c',
|
112
|
+
:default => 1
|
113
|
+
|
114
|
+
def benchmark(url)
|
115
|
+
req = Request.new(url, options[:method], options[:headers], self.class.body(options))
|
116
|
+
EM.run { Dispatcher.new(req, options[:concurrency]).run }
|
117
|
+
end
|
118
|
+
|
119
|
+
private
|
120
|
+
# Normalize the request body if its a file or a text string.
|
121
|
+
def self.body(options)
|
122
|
+
options[:file] ? File.read(options[:file]) : options[:body]
|
123
|
+
end
|
124
|
+
end
|
125
|
+
end
|
metadata
ADDED
@@ -0,0 +1,76 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: benchy
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Brad Gessler
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2012-02-01 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: em-http-request
|
16
|
+
requirement: &70165834461960 !ruby/object:Gem::Requirement
|
17
|
+
none: false
|
18
|
+
requirements:
|
19
|
+
- - ! '>='
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: '0'
|
22
|
+
type: :runtime
|
23
|
+
prerelease: false
|
24
|
+
version_requirements: *70165834461960
|
25
|
+
- !ruby/object:Gem::Dependency
|
26
|
+
name: thor
|
27
|
+
requirement: &70165834461540 !ruby/object:Gem::Requirement
|
28
|
+
none: false
|
29
|
+
requirements:
|
30
|
+
- - ! '>='
|
31
|
+
- !ruby/object:Gem::Version
|
32
|
+
version: '0'
|
33
|
+
type: :runtime
|
34
|
+
prerelease: false
|
35
|
+
version_requirements: *70165834461540
|
36
|
+
description: A dirty-simple HTTP benchmarking application
|
37
|
+
email:
|
38
|
+
- brad@bradgessler.com
|
39
|
+
executables:
|
40
|
+
- benchy
|
41
|
+
extensions: []
|
42
|
+
extra_rdoc_files: []
|
43
|
+
files:
|
44
|
+
- .gitignore
|
45
|
+
- Gemfile
|
46
|
+
- Rakefile
|
47
|
+
- benchy.gemspec
|
48
|
+
- bin/benchy
|
49
|
+
- lib/benchy.rb
|
50
|
+
- lib/benchy/version.rb
|
51
|
+
homepage: ''
|
52
|
+
licenses: []
|
53
|
+
post_install_message:
|
54
|
+
rdoc_options: []
|
55
|
+
require_paths:
|
56
|
+
- lib
|
57
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
58
|
+
none: false
|
59
|
+
requirements:
|
60
|
+
- - ! '>='
|
61
|
+
- !ruby/object:Gem::Version
|
62
|
+
version: '0'
|
63
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
64
|
+
none: false
|
65
|
+
requirements:
|
66
|
+
- - ! '>='
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '0'
|
69
|
+
requirements: []
|
70
|
+
rubyforge_project: benchy
|
71
|
+
rubygems_version: 1.8.11
|
72
|
+
signing_key:
|
73
|
+
specification_version: 3
|
74
|
+
summary: Benchmark HTTP applications
|
75
|
+
test_files: []
|
76
|
+
has_rdoc:
|