在 RoR 中,如果我没有得到服务器的响应,我该如何捕获异常?

In RoR, how do I catch an exception if I get no response from a server?

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

这是一个通用示例,您可以如何定义 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

我会考虑使用 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

说到拯救异常,你的目标应该是清楚地了解:

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

在您的代码中,获取内容的行也是可能出现网络错误的行:

content = open(url).read

如果您转到 documentation for the OpenURI module,您会看到它使用 Net::HTTP & friends 来获取任意 URI 的内容。

弄清楚 Net::HTTP 可以筹集的资金实际上非常复杂,但值得庆幸的是,其他人已经为您完成了这项工作。 Thoughtbot 的吊带项目有 lists of common network errors 可供您使用。请注意,其中一些错误与您所想的不同的网络条件有关,例如连接被重置。我认为也值得拯救它们,但请随意 trim 根据您的具体需求列出清单。

所以你的代码应该是这样的(跳过 Nokogiri 和 JSON 部分来简化一些事情): 需要 'net/http' 需要 '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