我编写了下面的抓取工具,从文件中获取网址列表并获取网页。问题是,在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."
答案 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是否有起点。
需要注意的另一个问题是当您启动并发连接时收益递减的情况。当您添加更多线程时,您可以达到吞吐量不会增加的点,因此尝试一些低数量的并发连接是一个很好的练习,然后开始提高限制,直到您看到吞吐量不再提高为止。
我还建议使用数据库来跟踪文件队列。您正在访问另一台服务器以检索这些文件,并且必须在运行开始时再次检索相同的文件,这对您和服务它们的人来说是一个很大的时间和资源浪费。在作业开始时,运行数据库并查找尚未检索的任何文件,抓取它们并设置其“已下载”标志。如果你启动并且已经下载了所有文件,你就知道上一次运行是成功的,所以清除它们并从列表的开头运行。你需要花一些时间来弄清楚这个数据库需要什么,但是,如果你的需求增长,你的运行时间会增加,你会遇到你一天中大部分时间都在运行的时间。停电或系统崩溃。你不想在那时开始。与通过互联网进行的慢速文件传输相比,使用数据库没有速度损失。