ActiveResource EOFError на "медленном" API

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

У меня есть два приложения на 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 ответа

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

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

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

Другие вопросы по тегам