Skip to content

Instantly share code, notes, and snippets.

@kinnalru
Created February 25, 2021 08:40
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save kinnalru/402798371f95f12cda0955538a9c30ef to your computer and use it in GitHub Desktop.
Save kinnalru/402798371f95f12cda0955538a9c30ef to your computer and use it in GitHub Desktop.
Скрипт для бенчмарка кешированных HTTP-соединений в ruby
require 'benchmark'
require 'timeout'
require 'logger'
require 'net/http'
require 'webrick'
require 'terminal-table'
require 'thin'
require 'colorize'
class Base
def initialize(port)
@port = port
end
def addr
"http://localhost:#{@port}"
end
end
class StartWebrick < Base
def start
server = WEBrick::HTTPServer.new Host: '0.0.0.0', Port: @port, MaxClients: 3000, ShutdownSocketWithoutClose: true, Logger: Logger.new(IO::NULL), AccessLog: []
server.mount_proc('/') { |req, res| res.body = 'hello' }
server.start
end
def stop(pid)
Process.kill("KILL", pid)
end
end
class StartThin < Base
def start
app = ->(env) { ['200', {'Content-Type' => 'text/html'}, ['hello']] }
Thin::Logging.silent = true
server = Thin::Server.new('0.0.0.0', @port, app, {})
server.maximum_connections = 3000
server.maximum_persistent_connections = 3000
server.threaded = true
server.start
end
def stop(pid)
Process.kill("KILL", pid)
end
end
class StartWebrickDocker < Base
def start
cmd = "
require \"webrick\"
require \"logger\"
WEBrick::HTTPServer.new(Host: \"0.0.0.0\", Port: 80, MaxClients: 3000, ShutdownSocketWithoutClose: true, Logger: Logger.new(IO::NULL), AccessLog: []).start"
exec "docker run --rm -p #{@port}:80 --name http ruby:2.5-alpine /usr/local/bin/ruby -e '#{cmd}'"
end
def stop(_pid)
system("docker rm -f http &> /dev/null")
end
end
class StartNginxDocker < Base
def start
exec "docker run --rm -p #{@port}:80 --name http nginxdemos/hello &> /dev/null"
end
def stop(_pid)
system("docker rm -f http &> /dev/null")
end
end
class StartLocalNetwork < Base
def addr
"http://172.99.99.99:9999"
end
def start
end
def stop(_pid)
end
end
class StartGoogle < Base
def addr
"https://google.com"
end
def start
end
def stop(_pid)
end
end
def with_server klass, port
server = klass.new(port)
sleep 1
pid = fork { server.start }
sleep 2
yield(server.addr)
rescue => e
puts "Exception: #{e}"
puts e.backtrace.join("\n")
exit!(1)
ensure
server.stop(pid)
Process.waitpid(pid)
end
def make_requests_single(uri, duration)
count = 0
t = Process.clock_gettime(Process::CLOCK_MONOTONIC)
Net::HTTP.start(uri.hostname, uri.port, use_ssl: uri.scheme == 'https') do |http|
http.max_retries = 0
while (Process.clock_gettime(Process::CLOCK_MONOTONIC) - t) < duration do
data = "/#{count}"
http.get(uri + data).body
count += 1
end
end
count
end
def make_requests_each(uri, duration)
count = 0
t = Process.clock_gettime(Process::CLOCK_MONOTONIC)
while (Process.clock_gettime(Process::CLOCK_MONOTONIC) - t) < duration do
Net::HTTP.start(uri.hostname, uri.port, use_ssl: uri.scheme == 'https') do |http|
http.max_retries = 0
data = "/#{count}"
http.get(uri + data).body
count += 1
end
end
count
end
def make_requests(type, uri, duration)
if type == :single
make_requests_single(uri, duration)
elsif type == :each
make_requests_each(uri, duration)
else
raise "invalid requests type"
end
end
def threaded(count)
count.times.map do
Thread.new do
yield
end
end.map(&:value).sum
end
def multiple(n = 3, &block)
n.times.map(&block)
end
DURATION=5
REPEATS=1
THREADS=[1, 5]
SERVERS=[:Webrick, :WebrickDocker, :Thin, :NginxDocker, :LocalNetwork, :Google]
REQUESTS=[:single, :each]
def letsgo
results = {}
THREADS.each do |th|
SERVERS.each do |server|
klass = Object.const_get("Start#{server}")
REQUESTS.each do |rt|
results[th] ||= {}
results[th][server] ||= {}
print "TH[#{th}] ST[#{server}] RT[#{rt}]: "
result = multiple(REPEATS) do |i|
print "#{i} "
with_server(klass, 5678) do |uri|
threaded(th) do
make_requests(rt, URI(uri), DURATION)
end
end
end
results[th][server][rt] = result
puts "#{result}"
end
end
end
table = Terminal::Table.new do |t|
t.headings = ['Example / Connection', *REQUESTS.map{|rt| ["#{rt}", "#{rt} stats"]}.flatten]
THREADS.each do |th|
SERVERS.each do |st|
single_results = results[th][st][:single]
single_avg = single_results.sum / single_results.size
single_rps = (single_results.sum / single_results.size) / DURATION
each_results = results[th][st][:each]
each_avg = each_results.sum / each_results.size
each_rps = (each_results.sum / each_results.size) / DURATION
norm = single_avg > each_avg
single_str = ["#{single_results}", "avg:#{single_avg}".colorize(norm ? :green : :red).ljust(11) + "rps:#{single_rps}".rjust(9)]
each_str = ["#{each_results}", "avg:#{each_avg}".colorize(norm ? :red : :green).ljust(11) + "rps:#{each_rps}".rjust(9)]
t.add_row ["TH #{th} #{st}", *single_str, *each_str]
end
t.add_separator
end
t.style = { border_left: false, border_right: false, border_bottom: false }
end
puts table
puts ''
end
letsgo
@kinnalru
Copy link
Author

Запуск с моими настройками длится несколько минут и не забудьте изменить адрес сервра в локалке.
Screenshot_20210225_111557

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment