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 может быть проблемой на стороне сервера.
- единственная "документация", которую я мог найти здесь, это https://forums.aws.amazon.com/thread.jspa?threadID=33427&start=50&tstart=50, но это лучше, чем ничего
Каждый сервер решает, когда закрывать соединение. Это зависит от программного обеспечения на стороне сервера и его настроек. Вы не можете это контролировать.