Ruby Net::HTTP 执行已过期

dav*_*ung 3 ruby net-http

使用 Net::HTTP,我定期发现下面的代码从 StandardError 中拯救了一条“执行已过期”的消息,尽管来自访问的 URL 的 Web 服务器日志显示相应的响应被快速发送。当 Web 服务器日志显示响应时间超过 5 秒时,我通常会看到 Timeout::Error 中的代码救援。

什么情况会导致下面的代码从 StandardError 中拯救出“执行已过期”而不是从 Timeout::Error拯救出来?

此代码在一个相对古老的 Ruby 1.9.3 上的多线程程序中运行,该平台不支持较新版本的 Ruby。尽管该程序是多线程的,但显示的代码仅在单线程上运行。

begin
  connection = Net::HTTP.new(uri.host, uri.port)
  connection.open_timeout = 5
  connection.read_timeout = 5
  connection.start do |http|
    request = Net::HTTP::Post.new("/reader_events")
    request.body = body
    response = http.request(request)
  end
rescue StandardError => std_error
  log "error sending event to server: #{std_error}"
rescue Timeout::Error => error
  log "timeout sending event to server"
end
Run Code Online (Sandbox Code Playgroud)

sme*_*fju 6

这是因为如何rescue运作。查看Exception类的文档页面。基本上,您可以创建许多从单个异常继承的异常,并使用带有父类的救援来处理所有异常:

begin
  ...
rescue Exception => exception
  ...
end
Run Code Online (Sandbox Code Playgroud)

这段代码将拯救所有类型的异常作为Exception根(其他异常继承自它)。在您的情况下Timeout::Error继承自RuntimeError继承自StandardError

Timeout::Error.ancestors
  => [Timeout::Error, RuntimeError, StandardError, Exception, Object, PP::ObjectMixin, Kernel, BasicObject]
Run Code Online (Sandbox Code Playgroud)

结果是Exception

Timeout::Error.new.is_a?(StandardError)
  => true
Run Code Online (Sandbox Code Playgroud)

在您的情况下,另一件事是解释器将rescue从上到下检查每个语句。这意味着首先它会检查它是否exception是一种,StandardError然后它会移动到下一个rescue块。您应该始终列出rescue从最具体到最通用的块。

更改rescue块的顺序以修复代码。