3

我正在使用 Rails 4.2.3 和 Nokogiri 从网站获取数据。当我没有从服务器得到任何响应时,我想执行一个操作,所以我有:

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 连接,要么是因为服务器无法响应(或其他一些导致我没有得到任何响应的原因)。除了我如何处理这种情况之外,还有更通用的方法来考虑这种情况吗?我觉得还有很多其他我没有想到的异常类型。

4

3 回答 3

5

这是一个通用示例,您可以如何定义 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
于 2016-07-20T08:45:02.003 回答
4

在挽救异常时,您应该致力于清楚地了解:

  • 系统中的哪些行会引发异常
  • 当这些代码行运行时,幕后发生了什么
  • 底层代码可能引发哪些特定异常

在您的代码中,获取内容的行也是可以看到网络错误的行:

content = open(url).read

如果您查看 OpenURI 模块的文档,您会看到它使用Net::HTTP&friends 来获取任意 URI 的内容。

弄清楚Net::HTTP可以筹集的资金实际上非常复杂,但幸运的是,其他人已经为您完成了这项工作。Thoughtbot 的吊杆项目有您可以使用的常见网络错误列表。请注意,其中一些错误与您想象的网络条件不同,例如连接被重置。我认为拯救这些也是值得的,但可以根据您的具体需求随意调整列表。

所以你的代码应该是这样的(跳过 Nokogiri 和 JSON 部分以简化一些事情): require 'net/http' require 'open-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
于 2016-07-21T12:35:14.433 回答
1

我会考虑使用在短时间内引发异常的超时:

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
于 2016-07-20T20:22:05.043 回答