2015-02-24 80 views
2

我们似乎有一种情况,其中rescue Exception不捕捉特定的异常。“救援异常”不拯救超时::错误net_http

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

根据堆栈跟踪,未被捕获的异常表面上看起来是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#发送,用线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之外的堆栈跟踪退出或中断?

+0

你的代码中的哪个部分是实际引发的异常?堆栈轨迹中的线条都不对应于我所看到的任何内容。它确实引发了'Timeout :: Error',它从'Exception'继承。我根本无法重现这一点。 – Max 2015-02-24 16:26:36

+0

我在我的代码示例中添加了一条评论'这是引发异常的代码'(虽然我拼写的'异常'错误 - 修正了 – Avram 2015-02-25 16:34:29

+0

我很快就会转发这个问题,我现在看到为了简洁起见,我很抱歉 – Avram 2015-02-25 16:56:13

回答

1

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

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

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

+0

顺便说一句,这意味着你的问题的一个简单的解决方案是添加一个明确的'救援超时::错误'块**之前**救援SystemExit,中断_dst_daemon之一。rb_ – erasing 2015-02-25 18:43:51

+0

真棒,非常感谢你 - 它并没有跨过我的脑海,看看Timeout :: Error是否来自我明确捕获的异常之一。 – Avram 2015-02-27 17:57:52