2016-07-14 40 views
1

我正在使用Rails 4.2.3和Nokogiri从网站获取数据。我想执行一个动作时,我没有得到来自服务器的响应,所以我有:在RoR中,如果我没有收到服务器的响应,如何捕获异常?

begin 
    content = open(url).read 
    if content.lstrip[0] == '<' 
    doc = Nokogiri::HTML(content) 
    else 
    begin 
     json = JSON.parse(content) 
    rescue JSON::ParserError => e 
     content 
    end 
    end 
rescue Net::OpenTimeout => e 
    attempts = attempts + 1 
    if attempts <= max_attempts 
    sleep(3) 
    retry 
    end 
end 

请注意,这不是从服务器得到一个500不同。我只想在没有得到任何响应时重试,因为我没有得到TCP连接,或者因为服务器没有响应(或者其他导致我没有得到任何响应的原因)。考虑到这种情况,除了我如何拥有它之外,还有更通用的方法吗?我觉得有很多其他的异常类型我没有想到。

+0

你不使用Nokogiri获取数据,您正在使用它来_parse_数据。 OpenURI“获取”数据。这是将Nokogiri从问题中删除的重要区别。此外,标题是误导性的; Rails是用Ruby编写的框架。你不用Rails编写东西,用Ruby编写它们,有时使用Rails的方法。我建议根据这些知识重新说明问题。您可以处理很多已定义的HTTP错误,并且_can_可以是由网站管理员定义的自定义HTTP错误,因此您必须注意这些错误。 –

+0

我不关心网站定义的任何自定义消息 - 这意味着正在发回响应。我试图说明情况(只有情况),我根本得不到回应?是否清楚我所要求的 - 得到没有反应与得到回应表明其他条件? – Dave

+0

您担心没有获得TCP连接或获取TCP连接,但服务器没有响应。 –

回答

4

这是通用的例子中,你如何定义超时时长为HTTP连接,并且在任何错误的情况下进行多次重试而获取的内容(编辑)

require 'open-uri' 
require 'nokogiri' 

url = "http://localhost:3000/r503" 

openuri_params = { 
    # set timeout durations for HTTP connection 
    # default values for open_timeout and read_timeout is 60 seconds 
    :open_timeout => 1, 
    :read_timeout => 1, 
} 

attempt_count = 0 
max_attempts = 3 
begin 
    attempt_count += 1 
    puts "attempt ##{attempt_count}" 
    content = open(url, openuri_params).read 
rescue OpenURI::HTTPError => e 
    # it's 404, etc. (do nothing) 
rescue SocketError, Net::ReadTimeout => e 
    # server can't be reached or doesn't send any respones 
    puts "error: #{e}" 
    sleep 3 
    retry if attempt_count < max_attempts 
else 
    # connection was successful, 
    # content is fetched, 
    # so here we can parse content with Nokogiri, 
    # or call a helper method, etc. 
    doc = Nokogiri::HTML(content) 
    p doc 
end 
+0

这完全没有回答我的问题。您正在捕获抛出的任何类型的异常的异常,即使是来自服务器的响应404或503s也是如此。我想说明哪些情况(并且只有情况下)服务器无法到达或根本不发送任何响应。 – Dave

+0

@Dave你的问题在如何处理其他事情上有点含糊不清,但接近正确的答案。你想要拯救'SocketError's,而不是'Net :: OpenTimeout'你想要抓住'Net :: ReadTimeout'。如果我们不理解/读取响应,则只有在打开连接失败时才会捕获Net :: OpenTimeout。如果你不关心其他错误,就不要拯救'OpenURI :: HTTPError'。 – Azolo

+0

Dave我在第一条评论后更新了代码(以显示更细化的异常处理)。正如@Azolo所说,你可以根据自己的实际需求进行定制。 –

1

我会考虑使用一个Timeout是引发很短的时间后异常:

MAX_RESPONSE_TIME = 2 # seconds 
begin 
    content = nil # needs to be defined before the following block 
    Timeout.timeout(MAX_RESPONSE_TIME) do 
    content = open(url).read 
    end 

    # parsing `content` 
rescue Timeout::Error => e 
    attempts += 1 
    if attempts <= max_attempts 
    sleep(3) 
    retry 
    end 
end 
+0

谢谢。如果DNS不能解决相关主机的问题,那么上面的解释会是什么呢? – Dave

+0

'Timeout.timeout'在块内的代码花费比MAX_RESPONSE_TIME更长的时间运行时引发异常 - 无论花费的时间多长。如果DNS需要的时间太长,我的示例将覆盖该情况。如果DNS发生另一个异常失败,那么您将需要从该异常中拯救(对不起,我不确定在这种情况下引发了什么异常)。 – spickermann

+0

这不是一个糟糕的解决方案,'Net :: XTimeout'错误实际上是由于'Net'模块内部使用'Timeout'造成的。然而,这不包含的事实是,如果你有一个大的页面'open-uri'解析它,并将它全部加载到内存中,我已经看到这需要花费很长的时间。 – Azolo

3

当谈到救助例外,你的目标应该有一个清醒的认识:

  • 系统中哪些线路可以提高例外
  • 什么是引擎盖下回事时的代码运行的那些行
  • 可以通过底层代码

在你的代码中提出的一些具体什么异常,多数民众赞成在获取内容的行也是一个可以看到网络错误:

content = open(url).read 

如果你去documentation for the OpenURI module你会看到它使用Net::HTTP &朋友获取任意URI的内容。

搞清楚Net::HTTP可以提出什么其实很复杂,但幸好其他人已经为你做了这项工作。 Thoughtbot的吊带项目有lists of common network errors,您可以使用。请注意,这些错误中的一部分与您想到的不同网络条件有关,例如连接正在重置。我认为这也值得拯救,但随时可以根据您的具体需求调整列表。

因此,这里是你的代码应该是什么样子(跳过引入nokogiri和JSON部分简化事情有点): 需要“网/ HTTP” 需要“开URI”

HTTP_ERRORS = [ 
    EOFError, 
    Errno::ECONNRESET, 
    Errno::EINVAL, 
    Net::HTTPBadResponse, 
    Net::HTTPHeaderSyntaxError, 
    Net::ProtocolError, 
    Timeout::Error, 
] 
MAX_RETRIES = 3 

attempts = 0 

begin 
    content = open(url).read 
rescue *HTTP_ERRORS => e 
    if attempts < MAX_RETRIES 
    attempts += 1 
    sleep(2) 
    retry 
    else 
    raise e 
    end 
end 
相关问题