【发布时间】:2010-04-12 21:22:08
【问题描述】:
我的问题与How to rescue timeout issues (Ruby, Rails)有关。
这是从超时中拯救的常用方法:
def action
# Post using Net::HTTP
rescue Timeout::Error => e
# Do something
end
我想确定异常是在尝试连接到主机时引发的,还是在尝试从主机读取时引发的。这可能吗?
【问题讨论】:
标签: ruby
我的问题与How to rescue timeout issues (Ruby, Rails)有关。
这是从超时中拯救的常用方法:
def action
# Post using Net::HTTP
rescue Timeout::Error => e
# Do something
end
我想确定异常是在尝试连接到主机时引发的,还是在尝试从主机读取时引发的。这可能吗?
【问题讨论】:
标签: ruby
这是解决方案(在 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
【讨论】:
http.request_get 创建并使用了一个不继承超时变量的Net::HTTP 新实例。
Net:HTTP 的新实例。尽管如此,无论我将打开和读取超时设置为什么,它似乎都会超时大约 30 秒。
request_get 放在内部救援块中,并在外部救援块中添加对http.start 的调用,如下所示:gist.github.com/364868 更新您的答案,我会接受它。非常感谢马克!
如果您无法升级到 ruby 2.x,Marc-André Lafortune 的解决方案仍然是最好的。
从 2.x 开始,Timeout::Error 的子类将根据触发的超时产生:
Net::OpenTimeoutNet::ReadTimeout但是,read_timeout 行为在 2.x 上很奇怪,因为它似乎是您设置的值的两倍。 This article 解释了原因。
这是两个超时的测试(在 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
【讨论】: