【问题标题】:"rescue Exception" not rescuing Timeout::Error in net_http“救援异常”不救援超时::net_http 中的错误
【发布时间】:2015-02-24 15:21:19
【问题描述】:

我们似乎遇到rescue Exception 没有捕获特定异常的情况。

我正在尝试发送有关发生任何异常的电子邮件警报,然后继续处理。我们已经对有意退出进行了必要的处理。在提醒我们之后,我们希望循环继续进行。

根据堆栈跟踪,没有被捕获的异常表面上是Timeout::Error

这是堆栈跟踪,删除了对我的中间代码的引用(我的代码的最后一行是 request.rb:93):

/opt/ruby-enterprise/lib/ruby/1.8/timeout.rb:64:in `rbuf_fill': execution expired (Timeout::Error)
    from /opt/ruby-enterprise/lib/ruby/1.8/net/protocol.rb:134:in `rbuf_fill'
    from /opt/ruby-enterprise/lib/ruby/1.8/net/protocol.rb:116:in `readuntil'
    from /opt/ruby-enterprise/lib/ruby/1.8/net/protocol.rb:126:in `readline'
    from /opt/ruby-enterprise/lib/ruby/1.8/net/http.rb:2028:in `read_status_line'
    from /opt/ruby-enterprise/lib/ruby/1.8/net/http.rb:2017:in `read_new'
    from /opt/ruby-enterprise/lib/ruby/1.8/net/http.rb:1051:in `__request__'
    from /mnt/data/blueleaf/releases/20150211222522/vendor/bundle/ruby/1.8/gems/rest-client-1.6.7/lib/restclient/net_http_ext.rb:51:in `request'
    from /opt/ruby-enterprise/lib/ruby/1.8/net/http.rb:1037:in `__request__'
    from /opt/ruby-enterprise/lib/ruby/1.8/net/http.rb:543:in `start'
    from /opt/ruby-enterprise/lib/ruby/1.8/net/http.rb:1035:in `__request__'
    from /mnt/data/blueleaf/releases/20150211222522/vendor/bundle/ruby/1.8/gems/rest-client-1.6.7/lib/restclient/net_http_ext.rb:51:in `request'
    from /mnt/data/blueleaf/releases/20150211222522/app/models/dst/request.rb:93:in `send'
    [intermediate code removed]
    from script/dst_daemon.rb:49
    from script/dst_daemon.rb:46:in `each'
    from script/dst_daemon.rb:46
    from /opt/ruby-enterprise/lib/ruby/1.8/benchmark.rb:293:in `measure'
    from script/dst_daemon.rb:45
    from script/dst_daemon.rb:24:in `loop'
    from script/dst_daemon.rb:24
    from script/runner:3:in `eval'
    from /mnt/data/blueleaf/releases/20150211222522/vendor/bundle/ruby/1.8/gems/rails-2.3.14/lib/commands/runner.rb:46
    from script/runner:3:in `require'

这里是 request.rb#send,第 93 行带有注释:

  def send
    build

    uri = URI.parse([DST::Request.configuration[:prefix], @path].join('/'))
    https = Net::HTTP.new(uri.host, uri.port)
    https.use_ssl = true
    https.verify_mode = OpenSSL::SSL::VERIFY_NONE
    https_request = Net::HTTP::Post.new(uri.request_uri.tap{|e| debug_puts "\nURL: #{e}, host:#{uri.host}"})
    # line 93:
    https_request.body = request 
    response = https.request(https_request)
    # the rest should be irrelevant

这里是 dst_daemon.rb;第 49 行用注释表示,应该捕获除故意中断之外的任何内容的 rescue Exception 接近尾声:

DST::Request.environment = :production
class DST::Request::RequestFailed < Exception; end

Thread.abort_on_exception = true
SEMAPHORE = 'import/dst/start.txt' unless defined?(SEMAPHORE)
DEBUG_DST = 'import/dst/debug.txt' unless defined?(DEBUG_DST)
DEBUG_LOG = 'import/dst/debug.log' unless defined?(DEBUG_LOG)

def debug_dst(*args)
  File.open(DEBUG_LOG, 'a') do |f|
    f.print "#{Time.now.localtime}: "
    f.puts(*args)
  end if debug_dst?
end

def debug_dst?
  File.exist?(DEBUG_DST)
end

dst_ids = [Institution::BAA_DST_WS_CLIENT_ID, Institution::BAA_DST_WS_DEALER_ID]
institutions = Institution.find_all_by_baa_api_financial_institution_id(dst_ids)
DST::Collector.prime_key!

loop do
  begin
    if File.exist?(SEMAPHORE)
      debug_dst 'waking up...'

      custodians = InstitutionAccount.acts_as_baa_custodian.
        find_all_by_institution_id(institutions).select(&:direct?)
      good,bad = custodians.partition do |c|
        c.custodian_users.map{|e2|e2.custodian_passwords.count(:conditions => ['expired is not true']) == 1}.all?
      end
      if bad.present?
        msg = "  skipping: \n"
        bad.each do |c|
          msg += "    #{c.user.full_name_or_email}, custodian id #{c.id}: "
          c.custodian_users.each{|cu| msg += "#{cu.username}:#{cu.custodian_passwords.count(:conditions => ['expired is not true'])}; "}
          msg += "\n"
        end
        AdminSimpleMailer.deliver_generic_mail("DST Daemon skipping #{bad.size} connections", msg)
        debug_dst msg
      end

      Benchmark.measure do
        good.each do |custodian|
          begin
            debug_dst "  collecting for: #{custodian.name}, #{custodian.subtitle}, (#{custodian.id.inspect})"
            # line 49:
            DST::Collector.new(custodian, 0).collect!
          rescue DST::Request::PasswordFailed, DST::Request::RequestFailed  => e
            message = e.message + "\n\n" + e.backtrace.join("\n")
            AdminSimpleMailer.deliver_generic_mail("DST Daemon Connection Failed #{e.class.name}", message)
            debug_dst "  skipping, #{e.class}"
          end
        end
      end.tap{|duration| debug_dst "collection done, duration #{duration.real.to_f/60} minutes. importing" }

      DST::Strategy.new(Date.yesterday, :recompute => true).import!
      debug_dst 'import done.'

      rm SEMAPHORE, :verbose => debug_dst?
    else
      debug_dst 'sleeping.' if Time.now.strftime("%M").to_i % 5 == 0
    end
  rescue SystemExit, Interrupt
    raise
  rescue Exception => e
    message = e.message + "\n\n" + e.backtrace.join("\n")
    AdminSimpleMailer.deliver_generic_mail("DST Daemon Exception #{e.class.name}", message)
  ensure
    sleep 60
  end
end

除了 SystemExit 或 Interrupt 之外,该循环是否应该以堆栈跟踪退出?

【问题讨论】:

  • 您的代码在哪里引发了实际异常?堆栈跟踪中的所有行都与我看到的任何内容都不对应。它确实在提高Timeout::Error,它确实继承自Exception。我根本无法重现。
  • 我在我的代码示例This is the code that raises the exception 中添加了注释(尽管我拼写错误的“异常” - 已修复。
  • 我很快就会重新发布这个问题,我现在看到为了简洁起见,我过度编辑了原始情况。我很抱歉。
  • 我看到异常默认为 Class.new(ExitException).... 无论如何,当我在控制台中尝试此操作时,exception.class 是 Class,而 exception.class.parent是对象。 Class.new(ExitException) 实际上会创建一个继承自 ExitException 的匿名类。当然exception.class 是类。这同样适用于Exception.class,对吧?你真正想要的是exception.ancestors。这将证明异常是异常的子类。
  • 我已经重写了这个问题。如果有人觉得删除并重新发布此 b/c 会更好,请告知。

标签: ruby exception-handling rescue


【解决方案1】:

您可能已经知道,在救援块中调用raise 会向调用者抛出异常。 由于 Timeout::Error 是 ruby​​ 1.8* 中的 Interrupt,因此 net_http 引发的超时异常将在 rescue SystemExit, Interrupt 块中处理,而不是在以下 rescue Exception =&gt; e 中处理。

要验证Timeout::Error 是一个中断,只需评估Timeout::Error.ancestors。您从中得到的是 Timeout::Error 继承自的类的层次结构。

*在 ruby​​1.9 中不再是这种情况。

【讨论】:

  • 顺便说一句,这意味着解决您的问题的一个简单方法是在 dst_daemon.rb
  • 太棒了,非常感谢 - 我没有想到 Timeout::Error 是否源于我明确捕获的异常之一。
猜你喜欢
  • 2021-10-24
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-08-21
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多