Created
February 25, 2021 08:40
-
-
Save kinnalru/402798371f95f12cda0955538a9c30ef to your computer and use it in GitHub Desktop.
Скрипт для бенчмарка кешированных HTTP-соединений в ruby
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Запуск с моими настройками длится несколько минут и не забудьте изменить адрес сервра в локалке.