Ruby
HTTP
wget
performance

Ruby の HTTP リクエストを送る方法の性能比較

HTTP リクエストの性能比較をさまざまな Gem などで行ってみた。


net-http

一番標準的な Net::HTTP を使ってみる。


net-http.rb

require "net/http"

100.times do
res = Net::HTTP.start("www.yahoo.co.jp") do |http|
http.get "/"
end
res.body
end



open-uri

みんな大好き open-uri も試してみる。


open-uri.rb

require "open-uri"

100.times do
body = open("http://yahoo.co.jp", &:read)
end



persistent_http

persistent_http を使ってみた。


persistent-http.rb

require "persistent_http"

http = PersistentHTTP.new(
:name => "MyHTTPClient",
:pool_size => 3,
:pool_timeout => 5,
:warn_timeout => 0.25,
:force_retry => true,
:url => "http://www.yahoo.co.jp/"
)

100.times do
response = http.request
response.body
end



Net::HTTP (オブジェクト再利用)

標準ライブラリの Net::HTTP でオブジェクトを再利用したバージョン。


persistent-net-http.rb

require "net/http"

Net::HTTP.start("www.yahoo.co.jp") do |http|
100.times do
res = http.get "/"
res.body
end
end



wget

意表を突いて、 Open3 + wget も試してみるテスト。


wget.rb

require "open3"

100.times do
stdout, stderr, exitcode = Open3.capture3("wget -d -O - http://www.yahoo.co.jp")
end



excon

excon を使ってみた。

リポジトリ中に Benchmark の実装もあるが、気にせず、独自に検証。


excon.rb


require "excon"

connection = Excon.new("http://www.yahoo.co.jp", persistent: true)

100.times do
get_response = connection.get(path: "/")
get_response.body
end



curb

libcurl を使った実装の1つ curb を使ってみた。

今回の実装では凝ったことをしてみた。


curb.rb

require "curb"

curl = Curl::Easy.new

urls = ["http://www.yahoo.co.jp/"] * 100

multi = Curl::Multi.new

responses = Hash.new do |hash, k|
hash[k] = ""
end

urls.each do |url|
curl = Curl::Easy.new url do |easy|
easy.on_body do |data|
responses[url] << data; data.size
end
end
multi.add curl
end

multi.perform do
end

urls.each do |url|
responses[url]
end



em-synchrony

EventMachine を使った実装の1つ em-synchrony を使ってみた。


em-synchrony.rb

require "em-synchrony"

require "em-synchrony/em-http"
require "em-synchrony/fiber_iterator"

urls = [ "http://www.yahoo.co.jp/" ] * 100

responses = Enumerator.new do |y|
EM.synchrony do
concurrency = 1

EM::Synchrony::FiberIterator.new( urls, concurrency ).each do |url|
y << [ url, EventMachine::HttpRequest.new(url).get ]
end
EventMachine.stop
end
end

responses.each do |url, response|
end



typhoeus

libcurl を使った実装の1つtyphoeus を使ってみた。


typhoeus

require "typhoeus"

100.times do
Typhoeus.get("www.yahoo.co.jp/")
end



httparty

なんとなく楽しそうな印象の HTTParty も使ってみた。


httparty.rb

require "httparty"

100.times do
response = HTTParty.get('http://www.yahoo.co.jp/')
end



計測結果

実行時間は time コマンドで測定。

ファイル名
実行時間

net-htp.rb
11.712s

open-uri.rb
22.079s

persistent-http.rb
17.293s

persistent-net-htp.rb
16.401s

wget.rb
14.450s

excon.rb
25.299s

curb.rb
20.234s

em-synchrony.rb
8.906s

typhoeus.rb
8.909s

httparty.rb
12.472s

Faraday 系の Gem は内部の Gem を直接使う場合に

比べて遅そうなので、試していません。


考察

たとえ concurrency が 1 でも、EventMachine を使う em-synchrony が最速という結果は少し意外でした。

実行タイミングによる差も非常に大きく、時間帯によっては上記の 2倍くらいの時間となることもあるので、上記の結果の信頼性はかなり低いです。

また www.yahoo.co.jp ではなく、もっと違う WEB サーバで試すべきかもしれません。

ただし、やっていることはほとんどアタックなので、自重しましょう。

私の中では


  • 安定した実行性能(実行時間の標準偏差が小さいこと)

  • 手馴れていること

  • 異常時のデバッグログ取得などもやりやすいこと

  • コード量も短くなること

などから wget を Open3 で実行した結果を使うことにしました。

ちゃんちゃん。


追記

コメントをいただいたので、追加で typhoeus を試してみました。さらについでに HTTParty も試してみました。

typhoeus は速いですね。

機能も豊富そうな上に API も簡単で試してみる価値ありそうです。