为什么Ruby中的curl比命令行卷曲慢?

时间:2010-05-18 10:20:21

标签: ruby http curl download curb

我正在尝试下载超过1m的页面(以序列ID结尾的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页样本。使用上面的代码时,我在2分钟内得到1000。当我将所有URL写入文件并在shell中执行时:

cat list | xargs curl

我在两分钟内生成所有8000页。

事情是,我需要它在ruby代码中,因为还有其他监视和处理代码。

我试过了:

  • Curl :: Multi - 它以某种方式更快,但错过了50-90%的文件(不下载它们并且没有给出任何理由/代码)
  • 使用Curl :: Easy的多个线程 - 与单线程的速度大致相同

为什么重用Curl :: Easy比后续命令行curl调用慢,如何让它更快?或者我做错了什么?

我更愿意修复我的下载管理器代码,而不是以不同的方式为此案例下载。

在此之前,我正在调用命令行wget,我提供了一个包含URL列表的文件。但并未处理所有错误,也无法在使用URL列表时单独为每个URL指定输出文件。

现在在我看来,最好的方法是使用系统调用'curl'命令的多个线程。但是为什么我可以在Ruby中直接使用Curl?

下载管理器的代码在这里,如果它可能有帮助:Download Manager(我玩过超时,从未将其设置为各种值,它似乎没有帮助)

任何提示都表示赞赏。

6 个答案:

答案 0 :(得分:5)

这可能是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 }
  # ...
}

答案 1 :(得分:3)

因此,如果你没有设置on_body处理程序而不是curb将缓冲下载。如果您正在下载文件,则应使用on_body处理程序。如果要使用Ruby Curl下载多个文件,请尝试使用Curl :: Multi.dow​​nload接口。

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

答案 2 :(得分:1)

已经完成了基准测试,将curb与其他方法(如HTTPClient)进行了比较。几乎所有类别的获胜者都是HTTPClient。此外,还有一些记录的场景,其中curb在多线程场景中不起作用。

和你一样,我有你的经验。我在20多个并发线程中运行了curl的系统命令,它比在20多个并发线程中运行curb要强10 X。无论如何,我尝试过,总是如此。

此后我转而使用HTTPClient,差别很大。现在它运行速度与20个并发卷曲系统命令一样快,并且使用的CPU也更少。

答案 3 :(得分:0)

首先让我说我对Ruby几乎一无所知。

我所知道的是Ruby是一种解释性语言;毫不奇怪,它比为特定平台编译的高度优化的代码要慢。每个文件操作都可能会检查它curl没有。 “其他一些东西”会让事情变得更加缓慢。

您是否尝试过分析代码以查看大部分时间花在哪里?

答案 4 :(得分:0)

Stiivi,

Net::HTTP是否足以简单下载HTML页面?

答案 5 :(得分:0)

您没有指定Ruby版本,但1.8.x中的线程是用户空间线程,不是由OS调度的,因此整个Ruby解释器只使用一个CPU /核心。最重要的是,有一个全局解释器锁,也可能是其他锁,干扰并发。由于您正在尝试最大化网络吞吐量,因此可能未充分利用CPU。

产生与机器有内存的进程数量相同的多个进程,并限制对线程的依赖。