Skip to content

Instantly share code, notes, and snippets.

@elct9620
Last active August 16, 2018 08:01
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 elct9620/9651456ff3abf820065b1e1f98da37ff to your computer and use it in GitHub Desktop.
Save elct9620/9651456ff3abf820065b1e1f98da37ff to your computer and use it in GitHub Desktop.
require 'net/http'
require 'uri'
require 'socket'
require 'openssl'
require 'fiber'
require 'benchmark'
require 'benchmark/ips'
require 'benchmark/memory'
# :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
# :nodoc:
class AsyncHTTP
attr_reader :uri, :response, :selector
def initialize(url, selector = nil)
@uri = URI(url)
@selector = selector
@response = ''
end
def client
@socket ||= TCPSocket.new uri.hostname, uri.port
@client ||= OpenSSL::SSL::SSLSocket.new(@socket)
end
def get
client.connect
client.sync_close = true
send_request
read_response
_, @response = @response.split("\n\r")
yield response if block_given?
response
end
private
def send_request
client.puts "GET #{uri.path} HTTP/1.0"
client.puts "Host: #{uri.hostname}"
client.puts
end
def read_response
loop do
break if client.eof?
@response << client.read_nonblock(1)
end
rescue OpenSSL::SSL::SSLErrorWaitReadable
return read_coroutine if selector
retry
end
def read_coroutine
selector.wait_readable(client) do
read_response
end
end
end
# :nodo:
class Logger
attr_reader :stdout
def initialize(stdout = true)
@stdout = stdout
end
def puts(*args)
return unless stdout
STDOUT.puts(*args)
end
end
def async(url, type, currency, selector, logger)
AsyncHTTP.new(format(url, type: type, currency: currency), selector).get { |x| logger.puts x }
end
def plain(url, type, currency, logger)
logger.puts Net::HTTP.get(URI(format(url, type: type, currency: currency)))
end
def thread(url, type, currency, logger)
Thread.new { plain(url, type, currency, logger) }
end
MARKET = [
'https://api.bitfinex.com/v1/pubticker/%<type>s%<currency>s',
'https://bittrex.com/api/v1.1/public/getmarketsummary?market=%<currency>s-%<type>s',
'https://api.binance.com/api/v1/ticker/24hr?symbol=%<type>s%<currency>s',
'https://api.hitbtc.com/api/2/public/ticker/%<type>s%<currency>s'
]
PAIRS = [
['BTC', 'USD'],
['ETH', 'USD']
]
def make
MARKET.map do |url|
PAIRS.map do |type, currency|
yield url, type, currency
end
end.flatten
end
logger = Logger.new(false)
selector = Selector.new
# AsyncHTTP.new(format(ENDPOINT, type: 'BTC', currency: 'USD'), selector).get { |x| puts x }
# AsyncHTTP.new(format(ENDPOINT, type: 'ETH', currency: 'USD'), selector).get { |x| puts x }
# selector.run
Benchmark.ips do |x|
x.report('Fiber') do |count|
count.times do
make do |url, type, currency|
async(url, type, currency, selector, logger)
end
selector.run
sleep 5
end
end
x.report('Net::HTTP') do |count|
count.times do
make do |url, type, currency|
plain(url, type, currency, logger)
end
sleep 5
end
end
x.report('[P] Net::HTTP') do |count|
count.times do
make do |url, type, currency|
thread(url, type, currency, logger)
end.map(&:join)
sleep 5
end
end
x.compare!
end
Benchmark.memory do |x|
x.report('Fiber') do |count|
make do |url, type, currency|
async(url, type, currency, selector, logger)
end
selector.run
end
x.report('Net::HTTP') do |count|
make do |url, type, currency|
plain(url, type, currency, logger)
end
end
x.report('[P] Net::HTTP') do |count|
make do |url, type, currency|
thread(url, type, currency, logger)
end.map(&:join)
end
x.compare!
end
@elct9620
Copy link
Author

When we try to send more request, the Fiber way will use more memory than Thread way.
But we can increment the buffer read from OpenSSL, and it will reduce more memory than Thready. way.

https://gist.github.com/elct9620/9651456ff3abf820065b1e1f98da37ff#file-async-http-rb-L73

read_nonblock(1)

Change it to

read_nonblock(102400)

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