2010-04-12 6 views
7

Ma question concerne How to rescue timeout issues (Ruby, Rails).Comment indiquer une erreur de délai de connexion à partir d'une erreur de délai de lecture dans Ruby Net :: HTTP

est ici le chemin commun pour sauver d'un délai d'attente:

def action 
    # Post using Net::HTTP 
rescue Timeout::Error => e 
    # Do something 
end 

J'aimerais déterminer si l'exception a été soulevée tout en essayant de connecter à l'hôte, ou si elle a été élevée tout en essayant de lu de l'hôte. Est-ce possible?

Répondre

11

est ici la solution (après correction de Ben):

require "net/http" 
http = Net::HTTP.new("example.com") 
http.open_timeout = 2 
http.read_timeout = 3 
begin 
    http.start 
    begin 
    http.request_get("/whatever?") do |res| 
     res.read_body 
    end 
    rescue Timeout::Error 
    puts "Timeout due to reading" 
    end 
rescue Timeout::Error 
    puts "Timeout due to connecting" 
end 
+0

Marc, je veux vraiment que cela soit vrai, mais cela ne fonctionne pas pour moi . Je pense que c'est parce que 'http.request_get' crée et utilise une nouvelle instance de' Net :: HTTP' qui n'hérite pas des variables de délai. –

+0

Je le reprends, il ne crée pas une nouvelle instance de 'Net: HTTP'. Cependant, il semble que le délai d'attente soit d'environ 30 secondes, peu importe ce que je définis les délais d'attente d'ouverture et de lecture. –

+0

open_timeout fonctionne pour moi, sur ruby ​​1.8.7 et 1.9.2 dev. Il est plus difficile de tester read_timeout, et il se peut que ce soit par bloc de lecture, vous devriez vérifier le code. Pourtant, si vous mettez les deux à 0,1, il devrait expirer rapidement, non? En tout cas, ma réponse est toujours la façon valide de distinguer ce qui a causé le délai! –

0

solution de Marc-André Lafortune est toujours le meilleur si vous ne pouvez pas mettre à ruby ​​2.x.

A partir de 2.x, une sous-classe de Timeout::Error ressusciteront en fonction du délai d'attente a été déclenchée:

  • Net::OpenTimeout
  • Net::ReadTimeout

Cependant, le comportement est étrange read_timeout sur 2 .x, car il semble doubler la valeur que vous définissez.

Voici un test pour les deux délais (testé sur 1.8.7, 1.9.3, 2.1.2, 2.2.4).

EDIT: Le test open_timeout fonctionne sur Mac, mais sur Linux, le client reçoit une erreur "connection refused".

require "net/http" 
require "socket" 

SERVER_HOST = '127.0.0.1' 
SERVER_PORT = 9999 

def main 
    puts 'with_nonlistening_server' 
    with_nonlistening_server do 
    make_request 
    end 

    puts 
    puts 'with_listening_server' 
    with_listening_server do 
    make_request 
    end 
end 

def with_listening_server 
    # This automatically starts listening 
    serv = TCPServer.new(SERVER_HOST, SERVER_PORT) 
    begin 
    yield 
    ensure 
    serv.close 
    end 
end 

def with_nonlistening_server 
    raw_serv = Socket.new Socket::AF_INET, Socket::SOCK_STREAM, 0 
    addr  = Socket.pack_sockaddr_in SERVER_PORT, SERVER_HOST 

    # Bind, but don't listen 
    raw_serv.bind addr 
    begin 
    yield 
    ensure 
    raw_serv.close 
    end 
end 

def make_request 
    http = Net::HTTP.new(SERVER_HOST, SERVER_PORT) 
    http.open_timeout = 1 
    http.read_timeout = 1 # seems to be doubled on ruby 2.x 
    start_tm = Time.now 
    begin 
    http.start 
    begin 
     http.get('/') 
    rescue Timeout::Error => err 
     puts "Read timeout: #{err.inspect}" 
    end 
    rescue Timeout::Error => err 
    puts "Open timeout: #{err.inspect}" 
    end 
    end_tm = Time.now 
    puts "Duration (sec): #{end_tm - start_tm}" 
end 

if __FILE__ == $PROGRAM_NAME 
    main 
end 

Exemple de sortie sur 1.9.3:

with_nonlistening_server 
Open timeout: #<Timeout::Error: execution expired> 
Duration (sec): 1.002477 

with_listening_server 
Read timeout: #<Timeout::Error: Timeout::Error> 
Duration (sec): 1.00599 

Exemple de sortie sur 2.1.2:

with_nonlistening_server 
Open timeout: #<Net::OpenTimeout: execution expired> 
Duration (sec): 1.005923 

with_listening_server 
Read timeout: #<Net::ReadTimeout: Net::ReadTimeout> 
Duration (sec): 2.009582 
Questions connexes