Назад | Перейти на главную страницу

Ответ HTTP 502, сгенерированный прокси-сервером после попытки отправить данные в восходящем направлении к частично закрытому соединению (пакет сброса)

Я получаю спорадические сообщения 502, возвращаемые прокси-сервером. При проверке потока пакетов я вижу, что nginx отправляет запрос POST в сокет, которому исходный сервер уже отправил [FIN, ACK]. Я хочу понять, как это возможно, и какие возможные решения. Это проблема с источником (он отправляет FIN, ACK только через 5 секунд после отправки ответа) или с прокси?

Вот снимок экрана PCAP, который иллюстрирует проблему:

Мое понимание:

Вопрос:

Я не владею происхождением - поэтому снимать там не могу.

Дополнительные детали:

Журнал ошибок на прокси (журнал ошибок nginx):

2017/04/17 06:51:07 [error] 123091#0: *225010841 upstream prematurely closed connection while reading response header from upstream, client: X.90.10, server: www.example.com, request: "POST /web/?a=b HTTP/1.1", upstream: "http://X.32.238:80/web/?a=b", host: "www.example.com"

Номера SEQ и ACK показаны для последних запросов на этом снимке экрана:

Какое возможное объяснение этому случаю?

Состояние гонки между счетчиком простоя ~ 5 секунд на источнике и переменной активностью на стороне клиента. Третья задействованная переменная - это, конечно, сетевая задержка.

Похоже, что в источнике есть таймер простоя ~ 5 секунд, в то время как вашему клиенту требуется ~ 5 секунд, чтобы сделать второй запрос (POST) через прокси-сервер Nginx. Если первое длиннее второго (включая задержку в сети), у вас нет проблем. Если для отправки клиентского запроса требуется совсем немного больше, то у вас проблема.

Вы можете увидеть, как POST и FIN, ACK от Nginx отправляются в значительной степени вместе: 2,4 мс и 2,6 мс после FIN, ACK источника соответственно. Это могло сбить вас с пути, потому что я вообще не думаю, что POST является ответом на FIN, ACK источника. Поскольку он отправляется через 2,4 мс после FIN источника, ACK

Почему прокси (nginx) отправляет другой запрос, если он уже получил FIN и фактически подтверждает его! (номер подтверждения в пакете POST [PSH, ACK] на самом деле равен SEQ_NUMBER + 1 из [FIN, ACK] - поэтому он подтверждает фантомный бит FIN.

Номер ACK в пакете POST, скорее всего, относится к пакету "200 OK". После этого HTTP-ответа со стороны сервера не поступают дополнительные данные, поэтому любой ACK от клиента будет ACKing с тем же номером.

Обновить: Теперь мы знаем, что у пакета POST номер ACK увеличен на 1, поэтому Nginx знал о [FIN, ACK]. Дальнейшее расследование показывает, что это нормально: машина может отправить запрос и закончить его [FIN, ACK], если она не планирует продолжать соединение после того, как получит ответ от удаленной стороны, которая должна была отправить запрошенные данные обратно и закончить с продолжением процесса [FIN, ACK].

Это не меняет того факта, что существует состояние гонки, когда источник решил закрыть соединение после 5 секунд бездействия, таким образом игнорируя пакет POST, который приходит вскоре после (и даже отправляет RST обратно - хотя неясно, будет ли этот RST прислали независимо).

Каковы возможные причины того, что источник возвращает [FIN, ACK] только через 5 секунд, а не сразу? Тайм-аут чтения / тайм-аут простоя?

Вам не нужно сразу возвращать FIN, ACK, особенно после HTTP 1.1 и введения постоянных соединений. Эти ~ 5 секунд кажутся таймером простоя на источнике.

Здесь подтверждаются обе вещи: https://en.wikipedia.org/wiki/HTTP_persistent_connection - включая тайм-аут простоя по умолчанию 5 секунд в Apache 2.2 или новее.

Предлагаемые решения

Я не могу предложить решение, не зная больше о вашей инфраструктуре, но, грубо говоря, у вас есть несколько вариантов:

  • Выясните, почему клиенту требуется 5 секунд для отправки второго запроса. Недостатки: отнимает много времени и, вероятно, требует внесения изменений в приложение.
  • Увеличьте время ожидания источника (Apache?) До 10 секунд. Недостатки: проблемы с масштабированием, поскольку вы не используете больше ресурсов. Могут потребоваться изменения приложения, чтобы как можно скорее избавиться от соединений.
  • Не используйте повторно TCP-соединение для второго HTTP-запроса, передавая заголовок «Connection: Close». Недостатки: более высокая стоимость запроса, поскольку вам нужно установить новый сеанс TCP. Могут потребоваться изменения приложения для выдачи заголовка для всех запросов или изменений в Nginx, что приведет к отклонению от вашей конфигурации по умолчанию (увеличение затрат на администрирование).
  • Используйте опцию «keepalive» на Nginx внутри своей восходящей конфигурации, чтобы установить keepalive менее 5 секунд. Недостаток: много лишнего трафика / шума.

Надеюсь это поможет :)

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

Я думаю, что мы можем позволить вышестоящим серверам nginx поддерживать таймаут активности。 Здесь другой автор решает это , см. этот