2016-07-14 1 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 «получает» данные. Это важное различие, которое устраняет Нокогири из вопроса. Кроме того, заголовок вводит в заблуждение; Rails - это структура, написанная на Ruby. Вы не пишете вещи в Rails, вы пишете их в Ruby, а иногда используете методы Rails. Я бы предложил переформулировать вопрос на основе этих знаний. Существует множество определенных HTTP-ошибок, которые вы можете обрабатывать, и там _can_ быть настраиваемыми, определенными администраторами сайта, поэтому вам нужно будет знать об этом. –

+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

Это точно не отвечает на мой вопрос. Вы перехватываете исключение для любого типа исключенных типов, даже 404s или 503s, которые являются ответами от сервера. Я хочу учитывать случаи hte (и только случаи), когда сервер не может быть достигнут или вообще не отправляет никаких сообщений. – Dave

+0

@Dave Ваш вопрос немного неоднозначен в отношении того, как обращаться со всем остальным, но это близко к правильному ответу. Вы хотите спасти 'SocketError' и вместо' Net :: OpenTimeout' вы хотите поймать 'Net :: ReadTimeout'. 'Net :: OpenTimeout' только ловит, если не открывается соединение, если мы не понимаем/не читаем ответ. Просто не спасайте «OpenURI :: HTTPError», если вас не волнуют другие ошибки. – Azolo

+0

Dave I обновил код после вашего первого комментария (чтобы показать более подробную обработку исключений). Как сказал @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' фактически являются результатом внутреннего использования' Timeout' в модуле 'Net'. Однако это не учитывает тот факт, что если у вас есть большая страница '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 
Смежные вопросы