2011-02-28 65 views
0

我写了下面的抓取工具,从文件中获取URL列表并获取页面。问题在于,2个小时左右后,系统变得非常慢并几乎无法使用。该系统是8核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,这是线程安全的。

创建任何线程之前:

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 

而在螺纹:

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

问题可能与RAM有关。所有下载的文件在下载并保存后都会保留在内存中。 (我不知道它们是否是大文件,您可以在两个小时内通过互联网下载多少内容?)请尝试使用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 = nil”更快地释放它们,或者更好的是,将下载和写入子例程,以便res_http快速超出范围。 GC应该照顾其余的。 – 2011-03-01 00:00:23

1

对于这种类型的问题,我强烈建议你看看EventMachine。查看this示例,了解如何与EventMachine和Ruby并行访存URL。

0

当我有一大堆的URL来处理我用Typhoeus and Hydra。 Hydra可以很容易地一次处理多个请求。检查times.rb example的起点。

当您调整并发连接时,还有一点需要注意的是收益递减的情况。当添加更多线程时,您可以达到吞吐量不会增加的点,因此尝试少量并发连接是一个很好的练习,然后开始提高限制,直到您看到吞吐量不再提高。

我还建议使用数据库来跟踪你的文件队列。您正在使用另一台服务器来检索这些文件,并且必须在运行开始时重新开始并再次检索相同的文件,这对您以及为他们提供服务的人来说都是一个很大的时间和资源浪费。在作业开始时通过数据库运行并查找任何尚未检索到的文件,抓住它们并设置其“已下载”标志。如果您启动并且所有文件都已下载,则您知道上次运行成功,因此清除所有文件并从列表开始运行。你需要花一些时间来弄清楚在这样的数据库中需要什么,但是,如果你的需求增长,你的运行时间会增加,你会遇到你一天中大部分时间都在运行的时间,发生停电或系统崩溃。你不想在这一刻开始。与通过互联网进行慢速文件传输相比,使用数据库没有速度上的损失。