Ruby线程 - 资源不足

时间:2011-02-28 17:13:20

标签: ruby rubygems web-crawler

我编写了下面的抓取工具,从文件中获取网址列表并获取网页。问题是,在2个小时左右之后,系统变得非常慢并几乎无法使用。该系统是四核linux,8GB内存。有人可以告诉我如何解决这个问题。

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."

5 个答案:

答案 0 :(得分:3)

通常,您无法修改线程之间共享的对象,除非这些对象线程安全。我会用Queue实例替换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

答案 1 :(得分:1)

对于此类问题,我强烈建议您查看EventMachine。查看有关如何使用EventMachine和Ruby并行获取URL的this示例。

答案 2 :(得分:0)

问题可能在于RAM。下载并保存后,所有下载的文件都会保留在内存中。 (我不知道它们是不是大文件,您可以在2小时内通过互联网下载多少?)尝试使用GC.start清理内存。比如在文件开头添加这个:

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

请注意,GC.start会在运行时冻结所有其他运行线程的人。如果它打破了一些下载,请减少时间(清理的东西会少)。

答案 3 :(得分:0)

我不太了解管理内存或发现在Ruby中占用过多内存的内容(我希望我知道更多),但是你现在有100个线程在同一时间运行。也许你应该只有4或8个一次操作?

如果这不起作用,我在程序中采取的另一个方法就是将一些代码放入方法中。至少你会知道某些变量何时超出范围。

答案 4 :(得分:0)

当我要处理一堆网址时,我会使用Typhoeus and Hydra。 Hydra可以轻松一次处理多个请求。检查times.rb example是否有起点。

需要注意的另一个问题是当您启动并发连接时收益递减的情况。当您添加更多线程时,您可以达到吞吐量不会增加的点,因此尝试一些低数量的并发连接是一个很好的练习,然后开始提高限制,直到您看到吞吐量不再提高为止。

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