同时检查数据库中多个URL的URL(状态,即200,301,404)的最佳方法

时间:2011-01-28 20:54:00

标签: ruby database concurrency http-status

这就是我想要完成的事情。假设我有100,000个url存储在数据库中,我想检查每个URL的http状态并存储该状态。我希望能够在相当短的时间内同时完成这项工作。

我想知道这样做的最佳方法是什么。我曾考虑过与工人/消费者或某种模型一起使用某种队列,但我没有足够的经验来了解在这种情况下哪种方法最有效。

想法?

3 个答案:

答案 0 :(得分:4)

看一下非常有能力的Typhoeus and Hydra组合。这两者使得同时处理多个URL变得非常容易。

Times”示例可让您快速启动并运行。在on_complete块中,将代码写入数据库。您可以使用一个线程来构建和维护一个健康级别的排队请求,或者将一组号码排队,让它们全部运行完成,然后循环另一个组。这取决于你。

Paul Dix,原作者,talked about his design goals在他的博客上。

这是我编写的用于下载存档邮件列表的示例代码,因此我可以进行本地搜索。如果人们开始运行代码,我故意删除URL以防止网站遭受DOS攻击:

#!/usr/bin/env ruby

require 'nokogiri'
require 'addressable/uri'
require 'typhoeus'

BASE_URL = ''

url = Addressable::URI.parse(BASE_URL)
resp = Typhoeus::Request.get(url.to_s)
doc = Nokogiri::HTML(resp.body)

hydra = Typhoeus::Hydra.new(:max_concurrency => 10)
doc.css('a').map{ |n| n['href'] }.select{ |href| href[/\.gz$/] }.each do |gzip|
  gzip_url = url.join(gzip)
  request = Typhoeus::Request.new(gzip_url.to_s)

  request.on_complete do |resp|
    gzip_filename = resp.request.url.split('/').last
    puts "writing #{gzip_filename}"
    File.open("gz/#{gzip_filename}", 'w') do |fo|
      fo.write resp.body
    end  
  end
  puts "queuing #{ gzip }"
  hydra.queue(request)
end

hydra.run

在我几年前的MacBook Pro上运行代码,在不到20秒的时间内通过无线连接到76个文件,总共11MB。如果您只做HEAD次请求,那么您的吞吐量会更好。你会想要搞乱并发设置,因为有一个点,让更多的并发会话只会减慢你的速度并且不必要地使用资源。

我给它八分之一;这是一个很棒的节拍,我可以跳舞。


编辑:

检查删除网址时,您可以使用HEAD请求,也可以使用If-Modified-Since进行GET。他们可以为您提供可用于确定网址新鲜度的回复。

答案 1 :(得分:1)

我没有在Ruby中做任何多线程,只有Java,但看起来非常简单:http://www.tutorialspoint.com/ruby/ruby_multithreading.htm

根据你的描述,你不需要任何队列和工人(嗯,我相信你也可以这样做,但我怀疑你会得到很多好处)。只需在几个线程之间划分您的URL,让每个线程执行每个块并使用结果更新数据库。例如,创建100个线程,并为每个线程提供1000个数据库行的处理范围。

如果您更愿意处理流程而不是线程,您甚至可以创建100个单独的流程并将它们作为参数提供。

要获取URL状态,我认为你做了一个HTTP HEAD请求,我猜在ruby中是http://apidock.com/ruby/Net/HTTP/request_head

答案 2 :(得分:0)

work_queue gem是在应用程序中异步和并发执行任务的最简单方法。

wq = WorkQueue.new 10

urls.each do |url|
  wq.enqueue_b do
    response = Net::HTTP.get_response(uri)
    puts response.code
  end
end

wq.join