104, 'Соединение reset по ошибке сокета peer', или Когда закрытие сокета приводит к RST, а не FIN?
Мы разрабатываем веб-сервис Python и веб-сайт клиента параллельно. Когда мы делаем HTTP-запрос от клиента к службе, один вызов последовательно вызывает socket.error в socket.py, в read:
(104, 'Connection reset by peer')
Когда я слушаю с помощью wirehark, "хорошие" и "плохие" ответы выглядят очень похожими:
- Из-за размера заголовка OAuth запрос разбивается на два пакета. Служба отвечает как с помощью ACK
- Служба отправляет ответ, один пакет на заголовок (HTTP/1.0 200 OK, затем заголовок Date и т.д.). Клиент отвечает каждому из ACK.
- (Хороший запрос) сервер отправляет FIN, ACK. Клиент отвечает FIN, ACK. Сервер отвечает ACK.
- (неверный запрос) сервер отправляет RST, ACK, клиент не отправляет ответ TCP, socket.error возникает на стороне клиента.
Как веб-служба, так и клиент работают на блоке x86-64 Gentoo Linux, работающем под управлением glibc-2.6.1. Мы используем Python 2.5.2 внутри того же virtual_env.
Клиент - это приложение Django 1.0.2, которое вызывает запросы httplib2 0.4.0. Мы подписываем запросы с помощью алгоритма подписи OAuth, а токен OAuth всегда задает пустую строку.
В службе работает Werkzeug 0.3.1, которая использует Python wsgiref.simple_server. Я запускал приложение WSGI через wsgiref.validator без проблем.
Похоже, что это должно быть легко отлаживать, но когда я просматриваю хороший запрос со стороны службы, он выглядит так же, как неудачный запрос, в функции socket._socketobject.close(), превращая методы делегата в манекен методы. Когда метод send или sendto (не может запомнить, который) отключен, FIN или RST отправляются, и клиент начинает обработку.
"Соединение reset by peer", похоже, накладывает вину на службу, но я не доверяю httplib2. Может ли клиент быть виноват?
** Дальнейшая отладка - выглядит как сервер на Linux **
У меня есть MacBook, поэтому я попытался запустить службу на одном и на веб-сайте клиента на другом. Клиент Linux вызывает сервер OS X без ошибки (FIN ACK). Клиент OS X вызывает службу Linux с ошибкой (RST ACK и (54, "Соединение reset одноранговым узлом" )). Итак, похоже, что это служба, работающая в Linux. Это x86_64? Плохое glibc? wsgiref? Еще глядя...
** Дальнейшее тестирование - wsgiref выглядит flaky **
Мы пошли на производство с Apache и mod_wsgi, а сбрасываем соединение. См. Мой ответ ниже, но мой совет - зарегистрировать соединение reset и повторить попытку. Это позволит вашему серверу нормально работать в режиме разработки и прочно работать.
Ответы
Ответ 1
У меня была эта проблема. См. Проблема" Соединение Reset по протоколу Python > .
У вас есть (скорее всего) проблемы с малыми сроками, основанные на блокировке Python Global Interpreter Lock.
Вы можете (иногда) исправить это с помощью time.sleep(0.01)
, размещенного стратегически.
"Где?" ты спрашиваешь. Ударь меня. Идея состоит в том, чтобы обеспечить лучший поток concurrency внутри и вокруг клиентских запросов. Попробуйте поместить его непосредственно перед тем, как сделать запрос так, чтобы GIL был Reset, и интерпретатор Python мог очистить любые отложенные потоки.
Ответ 2
Не используйте wsgiref для производства. Используйте Apache и mod_wsgi, или что-то еще.
Мы продолжаем видеть, что эти соединения сбрасываются, иногда часто, с помощью wsgiref (бэкэнд, используемый тестовым сервером werkzeug и, возможно, другими, такими как тестовый сервер Django). Наше решение заключалось в регистрации ошибки, повторении вызова в цикле и сдаче после десяти сбоев. httplib2 пытается дважды, но нам нужно еще несколько. Кажется, что они приходят в пучки - добавление 1-го сна может устранить проблему.
Мы никогда не видели соединение reset при работе через Apache и mod_wsgi. Я не знаю, что они делают по-другому (возможно, они просто маскируют их), но они не появляются.
Когда мы попросили местное сообщество разработчиков помочь, кто-то подтвердил, что они видят много попыток соединения с wsgiref, которые уходят на производственный сервер. Там есть ошибка, но ее будет сложно найти.
Ответ 3
Я понимаю, что вы используете python, но я нашел эту статью Java полезной.
http://java.sun.com/javase/6/docs/technotes/guides/net/articles/connection_release.html
Ответ 4
Обычно вы получаете RST, если вы выполняете закрытие, которое не задерживается (т.е. данные могут быть отброшены стеком, если он не был отправлен, и ACK'd) и нормальный FIN, если вы позвольте близко задерживаться (т.е. закрытие ожидает, что данные в пути будут ACK'd).
Возможно, все, что вам нужно сделать, это установить, чтобы ваш сокет задерживался, чтобы вы удалили условие гонки между незатухающим закрытием, сделанным на сокете и прибывшими ACK?