2011-02-28 4 views
0

ファイルからURLのリストを取得してページを取得するために、以下のクローラを作成しました。問題は、2時間程度後に、システムが非常に遅く、ほとんど使用できなくなることです。システムは、8GB RAMを搭載したクアッドコアのLinuxです。誰かがこの問題を解決する方法を教えてもらえますか?Rubyスレッド - リソース不足

require 'rubygems' 
require 'net/http' 
require 'uri' 

threads = [] 
to_get = File.readlines(ARGV[0]) 

dir = ARGV[1] 
errorFile = ARGV[2] 

error_f = File.open(errorFile, "w") 

puts "Need to get #{to_get.length} queries ..!!" 
start_time = Time.now 

100.times do 
    threads << Thread.new do 
    while q_word = to_get.pop 
     toks = q_word.chop.split("\t") 

     entity = toks[0] 
     urls = toks[1].chop.split("::") 
     count = 1 

     urls.each do |url| 
     q_final = URI.escape(url) 
     q_parsed = URI.parse(q_final) 

     filename = dir+"/"+entity+"_"+count.to_s 

     if(File.exists? filename) 
      count = count + 1 
     else 
      begin 
      res_http = Net::HTTP.get(q_parsed.host, q_parsed.request_uri) 
      File.open(filename, 'w') {|f| f.write(res_http) } 
      rescue Timeout::Error 
      error_f.write("timeout error " + url+"\n") 
      rescue 
      error_f.write($!.inspect + " " + filename + " " + url+"\n") 
      end 
      count = count + 1 
     end 
     end 
    end 
    end 
end 

puts "waiting here" 

threads.each { |x| x.join } 
puts "finished in #{Time.now - start_time}" 
#puts "#{dup} duplicates found" 
puts "writing output ..." 
error_f.close() 
puts "Done." 

答えて

3

一般に、オブジェクトがスレッドセーフでない限り、スレッド間で共有されるオブジェクトは変更できません。私はto_getをスレッドセーフであるQueueのインスタンスに置き換えます。すべてのスレッドを作成する前に

to_get = Queue.new 
File.readlines(ARGV[0]).each do |url| 
    to_get.push url.chomp 
end 
number_of_threads.times do 
    to_get.push :done 
end 

とスレッドで:私はTyphoeus and Hydraを使用し処理するためのURLの束を持っている

loop do 
    url = to_get.pop 
    break if url == :done 
    ... 
end 
0

おそらくRAMに問題があります。ダウンロードしたすべてのファイルは、ダウンロードして保存した後、メモリに保存されます。 (私は彼らが大きなファイルであるかどうか分かりません。どれくらいインターネットから2時間以内にダウンロードできますか?)GC.startでメモリをきれいにしてみてください。

Thread.new do 
    while true 
    sleep(60*5) # 5 minutes 
    GC.start 
    end 
end 

注意を実行しながらGC.startがスレッドを実行している他のすべてを凍結すること:ファイルの先頭にこれを追加するような何か。ダウンロードが途切れている場合は、時間を節約してください(クリーンなものは少なくなります)。

0

私は、Rubyでメモリを使いすぎていることを知りません(私はもっと知りたいと思っています)。しかし、同時に100スレッドが同時に動いています。一度に4〜8しか操作しないでください。

これがうまくいかない場合は、プログラムの一部でコードをメソッドに入れることもできます。特定の変数が範囲外になったときに、少なくともその方法ではわかります。

+0

はい、それです。 100スレッドが動作すると、プログラムは最新の100ダウンロードをメモリに保持します。 res_httpがスコープからすぐに外れるように、ファイルを書き込んだ後に "res_http = nil"を使用するか、ダウンロードしてサブルーチンに書き込む方が速くなります。 GCは残りの部分を処理する必要があります。 –

1

この種の問題については、EventMachineをご覧ください。 thisに、EventMachineとRubyで並列にURLを取得する方法の例を確認してください。

0

。 Hydraは一度に複数のリクエストを処理しやすくします。開始点はtimes.rb exampleを確認してください。

あなたの同時接続を増やしていくにつれて、リターンが小さくなる場合があります。スレッド数を増やしてもスループットが向上しない場合があります。同時接続数を少なくし、スループットが向上しなくなるまで制限を上げることをお勧めします。

また、データベースを使用してファイルキューを追跡することをお勧めします。これらのファイルを取得するために別のサーバーを起動しているため、実行の開始時に開始して同じファイルを再度取得する必要があります。ジョブの開始時に、データベースを検索し、取り出されていないファイルを探して、それらをつかんで「ダウンロード済み」フラグを設定します。あなたが起動し、すべてのファイルがダウンロードされている場合は、以前の実行が成功したことを知っているので、すべてをクリアしてリストの先頭から実行してください。あなたはそのようなデータベースに何が必要であるかを理解するために時間を費やす必要がありますが、ニーズが大きくなると実行時間が長くなり、1日のうちに実行した時間に遭遇します。停電やシステムクラッシュがある。その時点で最初から始める必要はありません。インターネットでのファイル転送が遅い場合と比較して、データベースを使用する場合のスピード違反はありません。

関連する問題