Почему curl в Ruby медленнее, чем curl командной строки?
Я пытаюсь загрузить более 1m страниц (url-адреса, заканчивающиеся идентификатором последовательности). Я реализовал своего рода многоцелевой менеджер загрузки с настраиваемым количеством потоков загрузки и одним потоком обработки. Загрузчик загружает файлы пакетами:
curl = Curl::Easy.new
batch_urls.each { |url_info|
curl.url = url_info[:url]
curl.perform
file = File.new(url_info[:file], "wb")
file << curl.body_str
file.close
# ... some other stuff
}
Я попытался загрузить образец 8000 страниц. При использовании кода выше я получаю 1000 за 2 минуты. Когда я пишу все URL-адреса в файл и делаю в shell:
cat list | xargs curl
Я ген все 8000 страниц в два протокол.
дело в том, что мне нужно, чтобы он был в ruby-коде, потому что есть другой код мониторинга и обработки.
Я пробовал:
- Curl:: Multi-это как-то быстрее, но пропускает 50-90% файлов (не загружает их и не дает повода/кода)
- несколько потоков с завитком:: легко-примерно с той же скоростью, что и однопоточный
почему повторно используется Curl:: Easy медленнее, чем последующие вызовы curl командной строки и как могу я сделать это быстрее? Или что я делаю не так?
Я бы предпочел исправить свой код диспетчера загрузки, чем сделать загрузку для этого случая по-другому.
до этого я вызывал wget командной строки, который я предоставил файл со списком URL-адресов. Howerver, не все ошибки были обработаны, также не удалось указать выходной файл для каждого URL отдельно при использовании списка URL.
теперь мне кажется, что лучшим способом было бы использовать несколько потоки с системным вызовом команды "curl". Но почему, когда я могу использовать непосредственно Curl в Ruby?
код для менеджера скачать здесь, если это может помочь: Скачать (я играл с тайм-аутами, от не-установки его на различные значения, это, казалось, не помогло)
какие-то намеки оценили.
6 ответов
Это может быть подходящей задачей для Typhoeus
что-то вроде этого (непроверенных):
require 'typhoeus'
def write_file(filename, data)
file = File.new(filename, "wb")
file.write(data)
file.close
# ... some other stuff
end
hydra = Typhoeus::Hydra.new(:max_concurrency => 20)
batch_urls.each do |url_info|
req = Typhoeus::Request.new(url_info[:url])
req.on_complete do |response|
write_file(url_info[:file], response.body)
end
hydra.queue req
end
hydra.run
подумайте об этом, вы могли бы получить проблемы с памятью из-за огромного количества файлов. Один из способов предотвратить это-никогда не хранить данные в переменной, а вместо этого напрямую передавать их в файл. Вы могли бы использовать em-http-запрос для этого.
EventMachine.run {
http = EventMachine::HttpRequest.new('http://www.website.com/').get
http.stream { |chunk| print chunk }
# ...
}
Итак, если вы не установите обработчик on_body, curb будет буферизировать загрузку. Если вы загружаете файлы, вы должны использовать обработчик on_body. Если вы хотите загрузить несколько файлов с помощью Ruby Curl, попробуйте Curl:: Multi.скачать интерфейс.
require 'rubygems'
require 'curb'
urls_to_download = [
'http://www.google.com/',
'http://www.yahoo.com/',
'http://www.cnn.com/',
'http://www.espn.com/'
]
path_to_files = [
'google.com.html',
'yahoo.com.html',
'cnn.com.html',
'espn.com.html'
]
Curl::Multi.download(urls_to_download, {:follow_location => true}, {}, path_to_files) {|c,p|}
Если вы хотите просто скачать один файл.
Curl::Easy.download('http://www.yahoo.com/')
вот хороший ресурс:http://gist.github.com/405779
были сделаны тесты, которые сравнивали curb с другими методами, такими как HTTPClient. Победителем почти во всех категориях стал HTTPClient. Кроме того, были некоторые документированные сценарии, где curb не работает в многопоточных сценариях.
Как и у вас, у меня был ваш опыт. Я запускал системные команды curl в 20 + параллельных потоках, и это было 10 x быстрее, чем запуск curb в 20+ параллельных потоках. Что бы я ни делал, так было всегда.
с тех пор я переключился на HTTPClient, и разница огромная. Теперь он работает так же быстро, как 20 параллельных системных команд curl, и использует меньше процессора.
прежде всего позвольте мне сказать, что я почти ничего не знаю о Ruby.
Я знаю, что Ruby-это интерпретируемый язык; неудивительно, что он медленнее, чем сильно оптимизированный код, который был скомпилирован для определенной платформы. Каждая файловая операция, вероятно, будет иметь проверки вокруг него, что curl
не делает. "Кое-что другое" Еще больше замедлит процесс.
вы пробовали профилировать свой код, чтобы увидеть, где тратится большая часть времени?
Stiivi,
есть шанс, что Net:: HTTP было бы достаточно для простой загрузки HTML-страниц?
вы не указали версию Ruby, но потоки в 1.8.x-это потоки пользовательского пространства, не запланированные ОС, поэтому весь интерпретатор Ruby использует только один процессор/ядро. Кроме того, существует глобальная блокировка интерпретатора и, возможно, другие блокировки, препятствующие параллелизму. Поскольку вы пытаетесь увеличить пропускную способность сети, вы, наверное, справляться с ЦП.
создайте столько процессов, сколько у машины есть памяти, и ограничьте зависимость от потоков.