ActiveResource EOFError на «медленном» API - PullRequest
1 голос
/ 12 июня 2011

Я серьезно изо всех сил пытаюсь решить эту проблему, любая помощь будет признательна!

У меня есть два приложения на Rails, назовем их Client и Service, все очень просто, обычный интерфейс REST - вот основной сценарий:

  • Клиент отправляет в службу POST /resources.json запрос
  • Служба запускает процесс, который создает ресурс и возвращает идентификатор клиенту

Опять же, все очень просто, просто обработка сервиса занимает много времени и может занимать несколько минут.Если это происходит, EOFError вызывается на клиенте, ровно через 60 секунд после того, как был сделан запрос ( независимо от того, что ActiveResource :: Base.timeout установлен на ), в то время как служба правильно обработала запрос и отвечает с200/201.Вот что мы видим в журналах (в хронологическом порядке):

C 00:00:00: POST /resources.json
S 00:00:00: Received POST /resources.json => resources#create
C 00:01:00: EOFError: end of file reached
  /usr/ruby1.8.7/lib/ruby/1.8/net/protocol.rb:135:in `sysread'
  /usr/ruby1.8.7/lib/ruby/1.8/net/protocol.rb:135:in `rbuf_fill'
  /usr/ruby1.8.7/lib/ruby/1.8/timeout.rb:62:in `timeout'
  ...
S 00:02:23: Response POST /resources.json, 201, after 143s

Очевидно, что ответ службы никогда не доходил до клиента.Я проследил ошибку до уровня сокета и пересоздал сценарий в сценарии, где я открываю TCPSocket и пытаюсь получить данные.Поскольку я ничего не запрашиваю, я не должен ничего возвращать, и мой запрос должен истечь через 70 секунд (см. Полный скрипт внизу):

Timeout::timeout(70) { TCPSocket.open(domain, 80).sysread(16384) }

Это были результаты для нескольких доменов:

www.amazon.com     => Timeout after 70s
github.com         => EOFError after 60s
www.nytimes.com    => Timeout after 70s
www.mozilla.org    => EOFError after 13s
www.googlelabs.com => Timeout after 70s
maps.google.com    => Timeout after 70s

Как видите, некоторые серверы позволяли нам «ждать» полных 70 секунд, в то время как другие разорвали наше соединение, вызывая EOFErrors.Когда мы провели этот тест для нашего сервиса, мы (как ожидается) получили EOFError через 60 секунд.

Кто-нибудь знает, почему это происходит?Есть ли способ предотвратить это или продлить время ожидания на стороне сервера?Поскольку наш сервис продолжает «работать», даже после закрытия сокета, я предполагаю, что он должен быть прерван на уровне прокси-сервера?

Каждый совет будет принят с благодарностью!

PS: Полныйскрипт:

require 'socket'
require 'benchmark'
require 'timeout'

def test_socket(domain)
  puts "Connecting to #{domain}"
  message = nil
  time    = Benchmark.realtime do
    begin
      Timeout::timeout(70) { TCPSocket.open(domain, 80).sysread(16384) }
      message = "Successfully received data" # Should never happen
    rescue => e
      message = "Server terminated connection: #{e.class} #{e.message}"
    rescue Timeout::Error
      message = "Controlled client-side timeout"
    end
  end
  puts "  #{message} after #{time.round}s"
end

test_socket 'www.amazon.com'
test_socket 'github.com'
test_socket 'www.nytimes.com'
test_socket 'www.mozilla.org'
test_socket 'www.googlelabs.com'
test_socket 'maps.google.com'

Ответы [ 2 ]

2 голосов
/ 25 мая 2012

Я знаю, что это почти год, но в случае, если кто-то еще найдет это, я хотел бы добавить возможного виновника.

ELB Amazon прервет незанятые соединения через 60 секунд, поэтому, если вы используете EC2 за ELB, то ELB может быть проблемой на стороне сервера.

0 голосов
/ 12 июня 2011

Каждый сервер решает, когда закрывать соединение.Это зависит от программного обеспечения на стороне сервера и его настроек.Вы не можете это контролировать.

...