如何从Ruby的Net :: HTTP中的读取超时错误中判断连接超时错误

时间:2010-04-12 21:22:08

标签: ruby

我的问题与How to rescue timeout issues (Ruby, Rails)有关。

以下是从超时中解救的常用方法:

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

我想确定在尝试连接到主机时是否引发了异常,或者是否在尝试从主机读取时引发异常。这可能吗?

2 个答案:

答案 0 :(得分:11)

这是解决方案(在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

答案 1 :(得分:0)

如果你不能升级到ruby 2.x,Marc-AndréLafortune的解决方案仍然是最好的。

从2.x开始,将引发Timeout::Error的子类,具体取决于触发的超时:

  • Net::OpenTimeout
  • Net::ReadTimeout

但是,read_timeout行为在2.x上很奇怪,因为它似乎会使您设置的值加倍。

这是两个超时的测试(在1.8.7,1.9.3,2.1.2,2.2.4上测试)。

编辑:open_timeout测试适用于Mac,但在Linux上,客户端收到“连接被拒绝”错误。

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

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

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