这就是我想要完成的事情。假设我有 100,000 个 URL 存储在数据库中,我想检查每个 URL 的 http 状态并存储该状态。我希望能够在相当短的时间内同时执行此操作。
我想知道最好的方法是什么。我考虑过使用某种队列与工人/消费者或某种事件模型,但我真的没有足够的经验来知道在这种情况下什么最有效。
想法?
这就是我想要完成的事情。假设我有 100,000 个 URL 存储在数据库中,我想检查每个 URL 的 http 状态并存储该状态。我希望能够在相当短的时间内同时执行此操作。
我想知道最好的方法是什么。我考虑过使用某种队列与工人/消费者或某种事件模型,但我真的没有足够的经验来知道在这种情况下什么最有效。
想法?
看看非常强大的Typhoeus 和 Hydra组合。两者使得同时处理多个 URL 变得非常容易。
“ Times ”示例应该让您快速启动并运行。在on_complete
块中放置您的代码以将您的状态写入数据库。您可以使用线程来构建和维护处于健康级别的排队请求,或者排队设置一个数字,让它们全部运行到完成,然后循环另一个组。由你决定。
原作者 Paul Dix在他的博客上谈到了他的设计目标。
这是我为下载存档邮件列表而编写的一些示例代码,以便进行本地搜索。如果人们开始运行代码,我故意删除了该 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 秒的时间内通过无线到 DSL 提取了 76 个文件,总计 11MB。如果你只做HEAD
请求,你的吞吐量会更好。你会想要弄乱并发设置,因为有更多的并发会话只会减慢你的速度并不必要地使用资源。
我给它 8 分(满分 10 分);它的节拍很棒,我可以跟着它跳舞。
编辑:
检查删除 URL 时,您可以使用 HEAD 请求或带有If-Modified-Since
. 他们可以为您提供可用于确定 URL 新鲜度的响应。
我没有在 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。
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