Last active
August 29, 2015 14:14
-
-
Save ecaron/a03aa1215a22bd86082a to your computer and use it in GitHub Desktop.
Ruby script, from http://samsaffron.com/archive/2013/11/13/live-restarts-of-a-supervised-unicorn-process, that monitors zero downtime deploys
This file contains hidden or 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 "optparse" | |
| require "uri" | |
| require "net/http" | |
| ### | |
| # This script is taken from http://samsaffron.com/archive/2013/11/13/live-restarts-of-a-supervised-unicorn-process | |
| # It has been modified from the original to: | |
| # 1) Have more intelligent validation of the URL | |
| # 2) Support HTTPS requests | |
| # 3) Colorized output based on type of request | |
| # 4) Gracefully exits upon cntl-c | |
| ## | |
| # Taken from http://stackoverflow.com/a/16363159/34340 | |
| class String | |
| def green; "\033[32m#{self}\033[0m" end | |
| def bg_green; "\033[42m#{self}\033[0m" end | |
| def bg_red; "\033[41m#{self}\033[0m" end | |
| def bg_yellow; "\033[43m#{self}\033[0m" end | |
| end | |
| $stdout.sync = true | |
| duration = 10 | |
| per_second = 10 | |
| opts = OptionParser.new do |opts| | |
| opts.banner = "Usage: bench_web [options] url" | |
| opts.on("-t", "--time TIME", OptionParser::DecimalInteger, "Duration to run the test in seconds (default 10)") do |t| | |
| duration = t | |
| end | |
| opts.on("-p", "--per-second REQUESTS", OptionParser::DecimalInteger, "Max number of requests per second (default 10)") do |t| | |
| per_second = t.to_f | |
| end | |
| end | |
| opts.parse! | |
| if ARGV.length != 1 | |
| puts opts.banner | |
| puts | |
| exit(1) | |
| end | |
| if ARGV[0] !~ /\A#{URI::regexp(['http', 'https'])}\z/ | |
| puts opts.banner | |
| puts | |
| puts "Invalid URL" | |
| puts | |
| exit(1) | |
| end | |
| url = URI.parse(ARGV[0]) | |
| GC.disable | |
| finish_time = Time.now + duration | |
| results = [] | |
| http = Net::HTTP.new(url.host, url.port) | |
| if url.scheme === 'https' | |
| http.use_ssl = true | |
| http.verify_mode = OpenSSL::SSL::VERIFY_NONE | |
| end | |
| request = Net::HTTP::Get.new(url.request_uri) | |
| while (start=Time.now) < finish_time | |
| begin | |
| res = http.request(request) | |
| req_duration = Time.now - start | |
| results << {duration: req_duration, code: res.code, length: res.body.length} | |
| GC.enable | |
| GC.start | |
| GC.disable | |
| padding = (1 / per_second.to_f) - (Time.now - start) | |
| if res.code.to_i >= 200 && res.code.to_i < 300 | |
| print ".".green | |
| elsif res.code.to_i < 400 | |
| print res.code.bg_green + " " | |
| elsif res.code.to_i < 400 | |
| print res.code.bg_yellow + " " | |
| else | |
| print res.code.bg_red + " " | |
| end | |
| if padding > 0 | |
| sleep padding | |
| end | |
| rescue Interrupt | |
| finish_time = Time.now | |
| puts "\nExiting..." | |
| end | |
| end | |
| GC.enable | |
| puts | |
| puts "Results" | |
| puts "Total duration: #{duration} second#{duration==1?"":"s"}" | |
| puts "Total requests: #{results.length}" | |
| summary = results.group_by{|r| r[:code]}.map{|code, array| [code, array.count]}.sort{|a,b| a[1] <=> b[1]} | |
| failures = summary.map{|code, count| code == "200" ? 0 : count}.inject(:+) | |
| if failures > 0 | |
| puts "Estimated downtime: #{((failures.to_f * (1.to_f / per_second)) * 1000).to_i}ms" | |
| end | |
| puts | |
| puts "By status code: #{summary.map{|code,count| "[#{code}]x#{count} "}.join}" | |
| puts "" | |
| puts "Percentage of the successful requests served within a certain time (ms)" | |
| good_requests = results.find_all{|r| r[:code] == "200"}.map{|r| r[:duration]}.sort | |
| if good_requests.length > 0 | |
| [25,50,66,75,80,90,95,98,99,100].map{ |percentile| | |
| time = good_requests[((percentile.to_f / 100.0) * (good_requests.length-1)).to_i] | |
| puts " #{percentile}%\t\t#{(time * 1000).to_i}" | |
| } | |
| end |
Author
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Run this code like:
It also supports a -t parameter for defining how long it should run (defaults to 10 seconds), and a -p parameter for the maximum requests per second (defaults to 10)