7

100 万を超えるページ (シーケンス ID で終わる URL) をダウンロードしようとしています。設定可能な数のダウンロード スレッドと 1 つの処理スレッドを備えた一種の多目的ダウンロード マネージャーを実装しました。ダウンローダーはファイルをバッチでダウンロードします。

curl = Curl::Easy.new

batch_urls.each { |url_info|
    curl.url = url_info[:url]
    curl.perform
    file = File.new(url_info[:file], "wb")
    file << curl.body_str
    file.close
    # ... some other stuff
}

8000ページのサンプルをダウンロードしようとしました。上記のコードを使用すると、2 分で 1000 が得られます。すべての URL をファイルに書き込んでシェルで実行すると、次のようになります。

cat list | xargs curl

8000 ページすべてを 2 分で作成しました。

他の監視および処理コードがあるため、ルビーコードでそれを含める必要があります。

私が試してみました:

  • Curl::Multi - なんとなく高速ですが、ファイルの 50 ~ 90% が失われます (ファイルをダウンロードせず、理由/コードも提供しません)。
  • Curl::Easy を使用した複数のスレッド - シングル スレッドとほぼ同じ速度

再利用された Curl::Easy が後続のコマンド ラインの curl 呼び出しよりも遅いのはなぜですか? どうすれば高速化できますか? または、私が間違っていることは何ですか?

この場合、別の方法でダウンロードを行うよりも、ダウンロード マネージャーのコードを修正したいと考えています。

この前は、URL のリストを含むファイルを提供するコマンドライン wget を呼び出していました。ただし、すべてのエラーが処理されたわけではなく、URL リストを使用する場合、URL ごとに個別に出力ファイルを指定することもできませんでした。

「curl」コマンドへのシステムコールで複数のスレッドを使用するのが最善の方法であるように思えます。しかし、Ruby で直接 Curl を使用できるのに、なぜでしょうか?

ダウンロード マネージャーのコードはここにあります。ダウンロード マネージャー(タイムアウトをさまざまな値に設定しないことから試してみましたが、役に立たなかったようです)

ヒントをいただければ幸いです。

4

6 に答える 6

5

これはTyphoeusにとって適切なタスクになる可能性があります

このようなもの(テストされていない):

require 'typhoeus'

def write_file(filename, data)
    file = File.new(filename, "wb")
    file.write(data)
    file.close
      # ... some other stuff
end

hydra = Typhoeus::Hydra.new(:max_concurrency => 20)

batch_urls.each do |url_info|
    req = Typhoeus::Request.new(url_info[:url])
    req.on_complete do |response|
      write_file(url_info[:file], response.body)
    end
    hydra.queue req
end

hydra.run

考えてみると、膨大な量のファイルが原因でメモリの問題が発生する可能性があります。これを防ぐ1つの方法は、データを変数に格納せずに、ファイルに直接ストリーミングすることです。そのためにem-http-requestを使用できます。

EventMachine.run {
  http = EventMachine::HttpRequest.new('http://www.website.com/').get
  http.stream { |chunk| print chunk }
  # ...
}
于 2010-05-18T13:30:06.417 に答える
3

したがって、on_bodyハンドラーを設定しない場合、curbはダウンロードをバッファリングします。ファイルをダウンロードする場合は、on_bodyハンドラーを使用する必要があります。Ruby Curlを使用して複数のファイルをダウンロードする場合は、Curl::Multi.dow​​nloadインターフェースを試してください。

require 'rubygems'
require 'curb'

urls_to_download = [
  'http://www.google.com/',
  'http://www.yahoo.com/',
  'http://www.cnn.com/',
  'http://www.espn.com/'
]
path_to_files = [
  'google.com.html',
  'yahoo.com.html',
  'cnn.com.html',
  'espn.com.html'
]

Curl::Multi.download(urls_to_download, {:follow_location => true}, {}, path_to_files) {|c,p|}

単一のファイルをダウンロードしたい場合。

Curl::Easy.download('http://www.yahoo.com/')

ここに良いリソースがあります:http://gist.github.com/405779

于 2010-05-18T17:47:55.033 に答える
1

curb を HTTPClient などの他のメソッドと比較したベンチマークが行われています。勝者は、ほぼすべてのカテゴリで HTTPClient でした。さらに、マルチスレッドのシナリオで縁石が機能しないいくつかの文書化されたシナリオがありました。

あなたと同じように、私はあなたの経験をしました。curl のシステム コマンドを 20 以上の同時スレッドで実行したところ、curb を 20 以上の同時スレッドで実行するよりも 10 倍高速でした。何を試しても、これは常に当てはまりました。

それ以来、私は HTTPClient に切り替えましたが、その違いは非常に大きいです。今では、20 個の同時 curl システム コマンドで実行され、CPU の使用量も少なくなりました。

于 2013-01-11T22:47:25.397 に答える
0

まず、私はRubyについてほとんど何も知らないと言わせてください。

私が知っているのは、Rubyはインタプリタ言語であるということです。特定のプラットフォーム用にコンパイルされた高度に最適化されたコードよりも遅いのは当然のことです。すべてのファイル操作には、おそらくその周りにチェックがありますが、そうでcurlはありません。「他の何か」は物事をさらに遅くします。

コードのプロファイリングを試みて、ほとんどの時間が費やされている場所を確認しましたか?

于 2010-05-18T10:32:00.197 に答える
0

Ruby のバージョンを指定しませんでしたが、1.8.x のスレッドはユーザー空間のスレッドであり、OS によってスケジュールされていないため、Ruby インタープリター全体が 1 つの CPU/コアしか使用しません。その上に、グローバル インタープリター ロックがあり、おそらく他のロックもあり、同時実行性を妨げています。ネットワーク スループットを最大化しようとしているため、CPU を十分に活用していない可能性があります。

マシンがメモリを持っている限り多くのプロセスを生成し、スレッドへの依存を制限します。

于 2012-01-15T04:33:05.530 に答える
0

スティヴィ、

HTMLページの単純なダウンロードにはNet::HTTPで十分である可能性はありますか?

于 2010-05-18T11:21:51.263 に答える