"救援异常"不抢救net_http中的Timeout :: Error

时间:2015-02-24 15:21:19

标签: ruby exception-handling rescue

我们似乎遇到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之外的堆栈跟踪退出吗?

1 个答案:

答案 0 :(得分:1)

正如您可能已经知道的那样,在救援区内调用raise会将异常引发给调用者。 由于Timeout::Error是ruby 1.8 *中的Interrupt,因此net_http引发的超时异常将在rescue SystemExit, Interrupt块中处理,而不是在以下rescue Exception => e中处理。

要验证Timeout::Error是否为中断,请评估Timeout::Error.ancestors。你得到的是Timeout :: Error继承自。

类的层次结构

*在ruby1.9中不再是这种情况。