Почему curl в Ruby медленнее, чем curl из командной строки? - PullRequest
7 голосов
/ 18 мая 2010

Я пытаюсь загрузить более 1 млн страниц (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 в файл и делаю в оболочке:

cat list | xargs curl

Я генерирую все 8000 страниц за две минуты.

Дело в том, что мне нужно, чтобы оно было в коде ruby, потому что есть другой код для мониторинга и обработки.

Я пытался:

  • Curl :: Multi - как-то быстрее, но пропускает 50-90% файлов (не загружает их и не дает причины / кода)
  • несколько нитей с помощью Curl :: Easy - примерно с той же скоростью, что и однопоточная

Почему повторно используется Curl :: Easy медленнее, чем последующие вызовы curl из командной строки, и как я могу сделать это быстрее? Или что я делаю не так?

Я бы предпочел исправить код менеджера загрузок, чем выполнять загрузку для этого случая другим способом.

До этого я вызывал wget из командной строки, который мне предоставил файл со списком URL. Однако не все ошибки были обработаны, также не удалось указать выходной файл для каждого URL отдельно при использовании списка URL.

Теперь мне кажется, что лучшим способом было бы использовать несколько потоков с системным вызовом команды 'curl'. Но почему, когда я могу напрямую использовать Curl в Ruby?

Код для диспетчера загрузки здесь, если он может помочь: Диспетчер загрузки (я играл с тайм-аутами, не устанавливая его в различные значения, он не помог)

Любые подсказки приветствуются.

Ответы [ 6 ]

5 голосов
/ 18 мая 2010

Это может быть подходящей задачей для 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-request .

EventMachine.run {
  http = EventMachine::HttpRequest.new('http://www.website.com/').get
  http.stream { |chunk| print chunk }
  # ...
}
3 голосов
/ 18 мая 2010

Итак, если вы не установите обработчик on_body, то curb будет буферизовать загрузку.Если вы загружаете файлы, вы должны использовать обработчик on_body.Если вы хотите загрузить несколько файлов, используя Ruby Curl, попробуйте интерфейс Curl :: Multi.download.

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

1 голос
/ 12 января 2013

Были сделаны тесты, которые сравнили ограничения с другими методами, такими как HTTPClient. Победителем почти во всех категориях стал HTTPClient. Кроме того, были некоторые документированные сценарии, в которых ограничение не работает в многопоточных сценариях.

Как и ты, у меня был твой опыт. Я запускал системные команды curl в более чем 20 параллельных потоках, и это было в 10 раз быстрее, чем при запуске curb в более чем 20 параллельных потоках. Что бы я ни пытался, это всегда было так.

С тех пор я переключился на HTTPClient, и разница огромна. Теперь он работает так же быстро, как и 20 одновременных системных команд curl, и использует меньше ресурсов ЦП.

0 голосов
/ 15 января 2012

Вы не указали версию Ruby, но потоки в 1.8.x являются потоками пользовательского пространства, не запланированными ОС, поэтому весь интерпретатор Ruby когда-либо использует только один процессор / ядро. Кроме того, существует глобальная блокировка интерпретатора, а также, возможно, и другие блокировки, препятствующие параллелизму. Поскольку вы пытаетесь максимизировать пропускную способность сети, вы, вероятно, недостаточно используете процессоры.

Создает столько процессов, сколько памяти имеет машина, и ограничивает зависимость от потоков.

0 голосов
/ 18 мая 2010

Stiivi,

есть ли шанс, что Net :: HTTP будет достаточно для простой загрузки HTML-страниц?

0 голосов
/ 18 мая 2010

Сначала позвольте мне сказать, что я почти ничего не знаю о Ruby.

Что я знаю, так это то, что Ruby - это интерпретируемый язык; не удивительно, что он медленнее, чем сильно оптимизированный код, скомпилированный для конкретной платформы. Каждая файловая операция, вероятно, будет иметь проверки вокруг этого, что curl не делает. «Некоторые другие вещи» замедляют работу еще больше.

Вы пытались профилировать свой код, чтобы увидеть, где большую часть времени проводит?

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...