12
require 'net/http'

urls = [
  {'link' => 'http://www.google.com/'},
  {'link' => 'http://www.yandex.ru/'},
  {'link' => 'http://www.baidu.com/'}
]

urls.each do |u|
  u['content'] = Net::HTTP.get( URI.parse(u['link']) )
end

print urls

此代码以同步方式工作。第一个请求,第二个,第三个。我想异步发送所有请求并在所有请求urls完成后打印。

最好的方法是什么?Fiber 适合这种情况吗?

4

8 回答 8

16

我刚刚看到这个,一年多一点,但希望对于一些谷歌人来说还不算太晚......

Typhoeus是迄今为止最好的解决方案。它以一种非常优雅的方式包装了 libcurl。您可以将其设置max_concurrency为 200 左右而不会窒息。

关于超时,如果您向 Typhoeus 传递一个:timeout标志,它只会将超时注册为响应……然后您甚至可以将请求放回另一个 hydra 中,如果您愿意,可以再试一次。

这是用 Typhoeus 重写的程序。希望这对以后遇到此页面的任何人有所帮助!

require 'typhoeus'

urls = [
  'http://www.google.com/',
  'http://www.yandex.ru/',
  'http://www.baidu.com/'
]

hydra = Typhoeus::Hydra.new

successes = 0

urls.each do |url|
    request = Typhoeus::Request.new(url, timeout: 15000)
    request.on_complete do |response|
        if response.success?
            puts "Successfully requested " + url
            successes += 1
        else
            puts "Failed to get " + url
        end
    end
    hydra.queue(request)
end

hydra.run 

puts "Fetched all urls!" if successes == urls.length
于 2015-01-05T01:54:28.960 回答
15

这是一个使用线程的示例。

require 'net/http'

urls = [
  {'link' => 'http://www.google.com/'},
  {'link' => 'http://www.yandex.ru/'},
  {'link' => 'http://www.baidu.com/'}
]

urls.each do |u|
  Thread.new do
    u['content'] = Net::HTTP.get( URI.parse(u['link']) )
    puts "Successfully requested #{u['link']}"

    if urls.all? {|u| u.has_key?("content") }
      puts "Fetched all urls!"
      exit
    end
  end
end

sleep
于 2010-01-24T21:08:18.927 回答
6

我写了一篇关于这个主题的深入博客文章,其中包含一个与 8 月发布的有点相似的答案 - 但有一些关键区别:1) 跟踪“线程”数组中的所有线程引用。2) 使用“join”方法在程序结束时绑定线程。

require 'net/http'

# create an array of sites we wish to visit concurrently.
urls = ['link1','link2','link3']  
# Create an array to keep track of threads.
threads = []

urls.each do |u|  
  # spawn a new thread for each url
  threads << Thread.new do
  Net::HTTP.get(URI.parse(u))
    # DO SOMETHING WITH URL CONTENTS HERE
    # ...
    puts "Request Complete: #{u}\n"
  end
end

# wait for threads to finish before ending program.
threads.each { |t| t.join }

puts "All Done!"  

完整教程(和一些性能信息)可在此处获得:https ://zachalam.com/performing-multiple-http-requests-asynchronously-in-ruby/

于 2016-04-02T17:13:50.283 回答
1

这可以通过 C 库cURL来完成。存在该库的ruby​​ 绑定,但它似乎不支持开箱即用的此功能。但是,看起来有一个补丁添加/修复它(页面上提供了示例代码)。我知道这听起来不太好,但如果没有更好的建议,可能值得一试。

于 2010-01-24T20:12:55.653 回答
1

在您的帮助下,concurrent-ruby您可以同时处理数据:

require 'net/http'
require 'concurrent-ruby'

class Browser
  include Concurrent::Async

  def render_page(link)
    sleep 5
    body = Net::HTTP.get( URI.parse(link) )
    File.open(filename(link), 'w') { |file| file.puts(body)}
  end

  private

  def filename(link)
    "#{link.gsub(/\W/, '-')}.html"
  end
end

pages = [
  'https://www.google.com',
  'https://www.bing.com',
  'https://www.baidu.com'
].map{ |link| Browser.new.async.render_page(link) }.map(&:value)
于 2019-08-13T20:45:06.853 回答
0

这取决于你想在函数之后做什么。你可以用简单的线程来做到这一点:

见:http ://snipplr.com/view/3966/simple-example-of-threading-in-ruby/

于 2010-01-24T20:18:00.703 回答
0

您可以让不同的线程执行每个 Net::HTTP.get。并等待所有线程完成。

顺便说一句,打印 url 将打印链接和内容。

于 2010-01-24T20:24:06.033 回答
0

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

wq = WorkQueue.new 2 # Limit the maximum number of simultaneous worker threads

urls.each do |url|
  wq.enqueue_b do
    response = Net::HTTP.get_response(url)
    # use the response
  end
end

wq.join # All requests are complete after this
于 2015-06-19T19:12:44.717 回答