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

  • 56
    いいね
  • 2
    コメント
この記事は最終更新日から1年以上が経過しています。

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 も簡単で試してみる価値ありそうです。