2011-02-28 2 views
0

Я написал ниже, чтобы получить список URL-адресов из файла и получить страницы. Проблема заключается в том, что через 2 часа система становится очень медленной и почти непригодной для использования. Система представляет собой четырехъядерный Linux с 8 ГБ оперативной памяти. Может ли кто-нибудь сказать мне, как решить эту проблему.Рубиновые потоки - из-за нехватки ресурсов

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 

А в теме:

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

Проблема, возможно, в ОЗУ. Все загруженные файлы сохраняются в памяти после загрузки и сохранения. (Я не знаю, большие ли они файлы, сколько вы можете загрузить через 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 = nil» после записи файла или, еще лучше, поставить загрузку и запись в подпрограмме, чтобы res_http быстро выпал из области. GC должен позаботиться об остальном. –

1

Для таких проблем я настоятельно рекомендую вам взглянуть на EventMachine. Проверьте пример this о том, как извлекать URL-адреса в параллель с EventMachine и Ruby.

0

Когда у меня есть куча адресов для обработки я использую Typhoeus and Hydra. Hydra упрощает обработку нескольких запросов одновременно. Проверьте начальную точку times.rb example.

Что-то еще, на что можно обратить внимание - это случай уменьшения отдачи при одновременном подключении ваших параллельных соединений. Вы можете попасть в точку, где ваша пропускная способность не увеличивается, когда вы добавляете больше потоков, так что это хорошее упражнение, чтобы попробовать небольшое количество параллельных подключений, а затем начать повышать лимит, пока не увидите, что ваша пропускная способность больше не улучшается.

Я также рекомендую использовать базу данных для отслеживания очереди файлов. Вы нажимаете на другой сервер для извлечения этих файлов, и вам нужно начинать с начала прогона и снова извлекать одни и те же файлы, это большой трат времени и ресурсов для вас и того, кто его обслуживает. В начале задания запускайте базу данных и найдите файлы, которые не были извлечены, захватите их и установите их «загруженный» флаг. Если вы запустили и все файлы были загружены, вы знаете, что предыдущий запуск был успешным, поэтому очистите их все и запустите с самого начала списка. Вам нужно потратить некоторое время, чтобы выяснить, что должно быть в такой базе данных, но если ваши потребности возрастут, время выполнения увеличится, и вы столкнетесь с временами, которые вы использовали большую часть дня и имеют перебои в подаче электроэнергии или сбое системы. Вы не хотите начинать с самого начала в этот момент. Нет никакого штрафа за скорость использования базы данных по сравнению с медленными передачами файлов через Интернет.

Смежные вопросы