2010-01-24 17 views
8
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

Este código funciona en estilo síncrono. Primera solicitud, segunda, tercera. Me gustaría enviar todas las solicitudes de forma asincrónica e imprimir urls después de que todas estén hechas.Hacer múltiples solicitudes HTTP de forma asíncrona

¿Cuál es la mejor manera de hacerlo? ¿Fibra es adecuada para eso?

Respuesta

1

Esto se puede hacer con la biblioteca C cURL. Existe una ruby binding para esa biblioteca, pero no parece admitir esta funcionalidad de fábrica. Sin embargo, parece que hay a patch agregándolo/corrigiéndolo (el código de ejemplo está disponible en la página). Sé que esto no suena bien, pero podría valer la pena intentarlo si no hay mejores sugerencias.

0

usted podría tener un subproceso diferente ejecutar cada uno de los Net :: HTTP.get. Y solo espere a que todos los hilos terminen.

Las URL de impresión de BTW imprimirán tanto el enlace como el contenido.

12

Aquí hay un ejemplo con hilos.

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 
+0

parece que funciona. ¿Pero cómo matar el hilo si el servidor no responde después de 15 segundos? – NVI

+1

Puede usar 'Timeout.timeotu (20) do .... end'. Sin embargo, eso genera un error, por lo que tendrías que hacer algo con el flujo de tu programa, y ​​tener una forma de etiquetar que una solicitud ha sido completada, además de verificar si existe la clave 'content'. –

+0

¿Es Ruby's Net :: HTTP threadsafe? – Daniel777

11

Acabo de ver esto, un año y un poco más tarde, pero espero que no sea demasiado tarde para algunos googler ...

Typhoeus, con mucho, la mejor solución para esto. Envuelve libcurl de una manera realmente elegante. Puede configurar el max_concurrency hasta aproximadamente 200 sin que se ahogue.

Con respecto a los tiempos de espera, si pasa Typhoeus una bandera :timeout, solo registrará un tiempo de espera como respuesta ... y luego incluso puede volver a poner la solicitud en otra hidra para intentar nuevamente si lo desea.

Aquí está su programa reescrito con Typhoeus. ¡Espero que esto ayude a cualquiera que se encuentre con esta página más tarde!

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 
0

El work_queue joya es la forma más fácil de realizar tareas de forma asíncrona y al mismo tiempo en su aplicación.

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 
1

He escrito una entrada de blog en profundidad sobre este tema, que incluye una respuesta que es algo similar a la de días Publicado - pero con algunas diferencias clave: 1) Mantiene un registro de todas las referencias de hilos de matriz "hilo". 2) Utiliza el método "join" para atar subprocesos al final del programa.

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!" 

El tutorial completo (y un poco de información de rendimiento) está disponible aquí: https://zachalam.com/performing-multiple-http-requests-asynchronously-in-ruby/

Cuestiones relacionadas