Last active
August 18, 2018 08:51
-
-
Save elct9620/e2eadcb8cf431f30a1b080bdee4077a1 to your computer and use it in GitHub Desktop.
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
# frozen_string_literal: true | |
require 'net/http' | |
require 'fiber' | |
require 'socket' | |
require 'openssl' | |
require 'benchmark/ips' | |
require 'benchmark/memory' | |
require 'memory_profiler' | |
# :nodoc: | |
class Selector | |
def initialize | |
@readable = {} | |
end | |
def wait_readable(io) | |
Fiber.new do | |
@readable[io] = Fiber.current | |
Fiber.yield | |
yield | |
end.resume | |
end | |
def resume | |
readable, = IO.select(@readable.keys) | |
readable.each do |io| | |
fiber = @readable.delete(io) | |
fiber.resume | |
end | |
end | |
def run | |
resume until @readable.empty? | |
end | |
end | |
class Fiber | |
# :nodoc: | |
class HTTP | |
class Connection | |
attr_reader :hostname, :port, :ssl, :ready, :selector | |
def initialize(hostname, port, selector) | |
@selector = selector | |
@hostname = hostname | |
@port = port | |
@io = TCPSocket.new hostname, port | |
@socket = OpenSSL::SSL::SSLSocket.new(@io) | |
@ready = false | |
@response = StringIO.new | |
@buffer = Net::BufferedIO.new(@response) | |
@count = 0 | |
@requests = 0 | |
end | |
def connect | |
return if ready | |
@ready = true | |
@socket.sync_close = true | |
@socket.connect | |
end | |
def start(request) | |
@requests += 1 | |
connect | |
request.update_uri @hostname, @port, true | |
request['Host'] = "#{@hostname}:#{@port}" | |
request.exec @socket, '1.1', request.path | |
end | |
def wait_response(&block) | |
loop do | |
break if @requests == @count | |
@response.write(@socket.read_nonblock(4096)) | |
response(&block) | |
end | |
rescue IO::WaitReadable, EOFError | |
return if @requests == @count | |
@selector.wait_readable(@socket) do | |
wait_response(&block) | |
end | |
end | |
def response(&block) | |
@response.rewind | |
return if @response.eof? | |
response = Net::HTTPResponse.read_new(@buffer) | |
response.decode_content = true | |
response.reading_body(@buffer, true) do | |
yield response if block_given? | |
end | |
@count += 1 | |
end | |
def close | |
return unless ready | |
@socket.close | |
end | |
end | |
include Enumerable | |
def initialize | |
@selector = Selector.new | |
@pool = {} | |
end | |
def request(request) | |
conn = @pool[request.uri.hostname] ||= Connection.new(request.uri.hostname, request.uri.port, @selector) | |
conn.connect | |
conn.start(request) | |
end | |
def connect | |
return if @connected | |
@connected = true | |
@socket.connect | |
@socket.sync_close = true | |
end | |
def start(&block) | |
instance_exec(self, &block) | |
@pool.values.each(&:close) | |
end | |
def each(&block) | |
@pool.values.each do |conn| | |
conn.wait_response(&block) | |
end | |
@selector.run | |
end | |
end | |
end | |
# uri = URI('https://api.binance.com/api/v1/ticker/24hr?symbol=ETHUSDT') | |
# uri = URI('https://api.bitfinex.com/v1/pubticker/BTCUSD') | |
# uri2 = URI('https://api.bitfinex.com/v1/pubticker/ETHUSD') | |
uris = [ | |
'https://api.bitfinex.com/v1/pubticker/BTCUSD', | |
'https://api.bitfinex.com/v1/pubticker/ETHUSD', | |
'https://api.hitbtc.com/api/2/public/ticker/BTCUSD', | |
'https://api.hitbtc.com/api/2/public/ticker/ETHUSD', | |
'https://bittrex.com/api/v1.1/public/getmarketsummary?market=USD-BTC', | |
'https://bittrex.com/api/v1.1/public/getmarketsummary?market=USD-ETH', | |
'https://api.binance.com/api/v1/ticker/24hr?symbol=BTCUSDT', | |
'https://api.binance.com/api/v1/ticker/24hr?symbol=ETHUSDT' | |
] | |
uris5 = [ | |
URI('https://api.bitfinex.com/v1/pubticker/BTCUSD'), | |
URI('https://api.bitfinex.com/v1/pubticker/ETHUSD') | |
] | |
uris2 = [ | |
URI('https://api.hitbtc.com/api/2/public/ticker/BTCUSD'), | |
URI('https://api.hitbtc.com/api/2/public/ticker/ETHUSD') | |
] | |
uris3 = [ | |
URI('https://bittrex.com/api/v1.1/public/getmarketsummary?market=USD-BTC'), | |
URI('https://bittrex.com/api/v1.1/public/getmarketsummary?market=USD-ETH') | |
] | |
uris4 = [ | |
URI('https://api.binance.com/api/v1/ticker/24hr?symbol=BTCUSDT'), | |
URI('https://api.binance.com/api/v1/ticker/24hr?symbol=ETHUSDT') | |
] | |
# Fiber::HTTP.new.start do |http| | |
# uris.each do |uri| | |
# http.request(Net::HTTP::Get.new(URI(uri))) | |
# end | |
# | |
# http.each do |response| | |
# puts response.body | |
# end | |
# end | |
Benchmark.ips do |x| | |
x.report('Fiber') do |c| | |
c.times do | |
Fiber::HTTP.new.start do |http| | |
uris.each do |uri| | |
http.request(Net::HTTP::Get.new(URI(uri))) | |
end | |
http.each do |response| | |
# TODO | |
end | |
end | |
sleep 5 | |
end | |
end | |
x.report('Net::HTTP') do |c| | |
c.times do | |
uris.each do |uri| | |
Net::HTTP.get(URI(uri)) | |
end | |
sleep 5 | |
end | |
end | |
x.report('[K] Net::HTTP') do |c| | |
c.times do | |
Net::HTTP.start(uris5.first.hostname, uris5.first.port, use_ssl: true) do |http| | |
uris5.each do |uri| | |
http.request(Net::HTTP::Get.new(URI(uri))).body | |
end | |
end | |
Net::HTTP.start(uris2.first.hostname, uris2.first.port, use_ssl: true) do |http| | |
uris2.each do |uri| | |
http.request(Net::HTTP::Get.new(URI(uri))).body | |
end | |
end | |
Net::HTTP.start(uris3.first.hostname, uris3.first.port, use_ssl: true) do |http| | |
uris3.each do |uri| | |
http.request(Net::HTTP::Get.new(URI(uri))).body | |
end | |
end | |
Net::HTTP.start(uris4.first.hostname, uris4.first.port, use_ssl: true) do |http| | |
uris4.each do |uri| | |
http.request(Net::HTTP::Get.new(URI(uri))).body | |
end | |
end | |
sleep 5 | |
end | |
end | |
x.report('[P] Net::HTTP') do |c| | |
c.times do | |
uris.map do |uri| | |
Thread.new { Net::HTTP.get(URI(uri)) } | |
end.each(&:join) | |
sleep 5 | |
end | |
end | |
x.compare! | |
end | |
Benchmark.memory do |x| | |
x.report('Fiber') do | |
Fiber::HTTP.new.start do |http| | |
uris.each do |uri| | |
http.request(Net::HTTP::Get.new(URI(uri))) | |
end | |
http.each do |response| | |
# TODO | |
end | |
end | |
end | |
sleep 5 | |
x.report('Net::HTTP') do | |
uris.each do |uri| | |
Net::HTTP.get(URI(uri)) | |
end | |
end | |
sleep 5 | |
x.report('[K] Net::HTTP') do |c| | |
Net::HTTP.start(uris5.first.hostname, uris5.first.port, use_ssl: true) do |http| | |
uris5.each do |uri| | |
http.request(Net::HTTP::Get.new(URI(uri))).body | |
end | |
end | |
Net::HTTP.start(uris2.first.hostname, uris2.first.port, use_ssl: true) do |http| | |
uris2.each do |uri| | |
http.request(Net::HTTP::Get.new(URI(uri))).body | |
end | |
end | |
Net::HTTP.start(uris3.first.hostname, uris3.first.port, use_ssl: true) do |http| | |
uris3.each do |uri| | |
http.request(Net::HTTP::Get.new(URI(uri))).body | |
end | |
end | |
Net::HTTP.start(uris4.first.hostname, uris4.first.port, use_ssl: true) do |http| | |
uris4.each do |uri| | |
http.request(Net::HTTP::Get.new(URI(uri))).body | |
end | |
end | |
end | |
sleep 5 | |
x.report('[P] Net::HTTP') do | |
uris.map do |uri| | |
Thread.new { Net::HTTP.get(URI(uri)) } | |
end.each(&:join) | |
end | |
x.compare! | |
end | |
# MemoryProfiler.report do | |
# Fiber::HTTP.new.start do |http| | |
# uris.each do |uri| | |
# http.request(Net::HTTP::Get.new(URI(uri))) | |
# end | |
# | |
# http.each do |response| | |
# puts response.body | |
# end | |
# end | |
# end.pretty_print |
Author
elct9620
commented
Aug 18, 2018
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment