2010-01-24 34 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

Ten kod działa w stylu synchronicznym. Pierwsza prośba, druga, trzecia. Chcę wysyłać wszystkie żądania asynchronicznie i wydrukować urls po zakończeniu ich wszystkich.Tworzenie wielu żądań HTTP asynchronicznie

Jaki jest najlepszy sposób na zrobienie tego? Czy świat nadaje się do tego?

Odpowiedz

1

Można to zrobić za pomocą biblioteki C cURL. Istnieje biblioteka ruby binding dla tej biblioteki, ale wydaje się, że nie obsługuje tej funkcjonalności po wyjęciu z pudełka. Jednak wygląda na to, że istnieje a patch dodanie/naprawienie (przykładowy kod jest dostępny na stronie). Wiem, że to nie brzmi świetnie, ale warto spróbować, jeśli nie ma lepszych sugestii.

0

Można mieć inny wątek wykonać każdy z Net :: HTTP.get. I poczekaj aż wszystkie wątki się zakończą.

Adres URL drukowania BTW spowoduje wydrukowanie zarówno linku, jak i treści.

12

Oto przykład użycia wątków.

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

Wydaje się, że działa. Ale jak zabić wątek, jeśli serwer nie odpowie po 15 sekundach? – NVI

+1

Możesz użyć 'Timeout.timeotu (20) do .... end'. Wiąże się to jednak z błędem, więc musisz coś zrobić z przepływem programu i mieć możliwość oznaczania, że ​​żądanie zostało zakończone, inne niż sprawdzanie, czy istnieje klucz "content". –

+0

Czy sieć wątków Ruby :: HTTP jest bezpieczna? – Daniel777

11

Widziałem tylko ten, w roku, a nieco później, ale mam nadzieję, że nie jest zbyt późno dla niektórych Googler ...

Typhoeus zdecydowanie najlepsze rozwiązanie tego problemu. Zawijanie libcurl w naprawdę elegancki sposób. Możesz ustawić max_concurrency do około 200 bez dławienia.

W przypadku przekroczenia limitu czasu, jeśli przekażesz typhoeusowi flagę :timeout, zarejestruje on tylko czas oczekiwania jako odpowiedź ... a następnie możesz złożyć żądanie ponownie w innej hydrze, aby spróbować ponownie, jeśli chcesz.

Oto Twój program napisany ponownie z Typhoeus. Mam nadzieję, że pomoże to każdemu, kto natknie się na tę stronę później!

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

work_queue gem jest najprostszym sposobem, aby wykonywać zadania asynchronicznie i jednocześnie w swojej aplikacji.

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

ja napisałem blogu dogłębne na ten temat, która zawiera odpowiedź, która jest nieco podobna do jednej sierpnia pisał - ale z kilku kluczowych różnic: 1) śledzi wszystkie odesłania wątek w tablica "wątków". 2) Używa metody "join" do wiązania wątków na końcu programu.

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

Pełny poradnik (i niektóre informacje wydajność) jest dostępna tutaj: https://zachalam.com/performing-multiple-http-requests-asynchronously-in-ruby/

Powiązane problemy