代码之家  ›  专栏  ›  技术社区  ›  Stiivi

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

  •  7
  • Stiivi  · 技术社区  · 14 年前

    我正在尝试下载超过100万页(以序列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
    

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

    我试过:

    • Curl::Multi-虽然速度更快,但会丢失50-90%的文件(不会下载它们,也不会给出原因/代码)
    • 使用Curl::Easy的多线程-速度与单线程相同

    在此之前,我正在调用命令行wget,我提供了一个包含url列表的文件。然而,并不是所有的错误都得到了处理,而且在使用URL列表时也不可能为每个URL分别指定输出文件。

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

    下载管理器的代码在这里,如果可能有帮助的话: Download Manager

    谢谢你的任何暗示。

    6 回复  |  直到 14 年前
        1
  •  5
  •   Jonas Elfström    14 年前

    这对我来说可能是个合适的任务 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 }
      # ...
    }
    
        2
  •  3
  •   Georg Fritzsche    14 年前

    所以,如果你不设置一个on-body处理程序,那么curb会缓冲下载。如果你正在下载文件,你应该使用一个身体上的处理程序。如果要使用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

        3
  •  1
  •   Henley Wing Chiu    12 年前

    和你一样,我也有过你的经历。我在20多个并发线程中运行curl的系统命令,比在20多个并发线程中运行curb快10倍。不管我怎么努力,都是这样。

    从那以后,我转向了HTTPClient,两者的区别是巨大的。现在它的运行速度可以达到20个并发curl系统命令,并且使用更少的CPU。

        4
  •  0
  •   Alphax    14 年前

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

    curl 不。“一些其他的东西”会让事情更慢。

    你试过分析你的代码来看看大部分时间都花在哪里了吗?

        5
  •  0
  •   Vojto    14 年前

    有可能吗 Net::HTTP 只需下载HTML页面就足够了吗?

        6
  •  0
  •   Chris D    13 年前

    您没有指定Ruby版本,但是1.8.x中的线程是用户空间线程,不受操作系统调度,因此整个Ruby解释器只使用一个CPU/内核。除此之外,还有一个全局解释器锁,可能还有其他锁,它们会干扰并发性。由于您试图最大限度地提高网络吞吐量,因此可能没有充分利用CPU。

    产生尽可能多的进程,因为机器有内存,并限制对线程的依赖。