nginx + Jetty - тысячи соединений зависли в LAST_ACK

nginx + Jetty - тысячи соединений зависли в LAST_ACK

У меня есть машина FreeBSD с jails, в частности, двумя: одна, на которой запущен nginx, а другая, на которой запущена программа Java, принимающая запросы через Jetty (встроенный режим).

Jetty постоянно получает более 500 запросов в секунду, и в последнее время возникла проблема, из-за которой у меня постоянно будет более60,000соединения в состоянии LAST_ACK между nginx и jetty.

Распределение всех соединений (включая некоторые другие службы, в частности php-fpm)

root@host:/root # netstat -an > conns.txt
root@host:/root # cat conns.txt | awk '{print $6}' | sort | uniq -c | sort -n
18 LISTEN
112 CLOSING
485 ESTABLISHED
650 FIN_WAIT_2
1425 FIN_WAIT_1
3301 TIME_WAIT
64215 LAST_ACK

Распределение соединений nginx -> jetty

root@host:/root # cat conns.txt | grep '10.10.1.57' | awk '{print $6}' | sort | uniq -c | sort -n
1 
3 CLOSE_WAIT
3 LISTEN
18 FIN_WAIT_2
125 ESTABLISHED
64193 LAST_ACK

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

Некоторые из связей,

tcp4       0      0 10.10.1.50.46809       10.10.1.57.9050        LAST_ACK
tcp4       0      0 10.10.1.50.46805       10.10.1.57.9050        LAST_ACK
tcp4       0      0 10.10.1.50.46797       10.10.1.57.9050        LAST_ACK
tcp4       0      0 10.10.1.50.46794       10.10.1.57.9050        LAST_ACK
tcp4       0      0 10.10.1.50.46790       10.10.1.57.9050        LAST_ACK
tcp4       0      0 10.10.1.50.46789       10.10.1.57.9050        LAST_ACK
tcp4       0      0 10.10.1.50.46771       10.10.1.57.9050        LAST_ACK
etc..
  • На стороне Jetty я установил maxIdleTimeзначение 2000 — до этого все соединения были, ESTABLISHEDно теперь ониLAST_ACK
  • На стороне причала я установил Connection: close(т.е. response.setHeader(HttpHeaders.CONNECTION, HttpHeaderValues.CLOSE);)
  • Jetty никогда не сообщает о большом количестве открытых соединений — всегда их очень мало.
  • PF/IPFW в настоящее время не используется.
  • nginx - reset_timedout_connectionвключен

Я не могу понять, как заставить nginx или jetty принудительно закрыть соединение. Может быть, это просто нужно исправить в Jetty, чтобы он полностью закрывал сокет после завершения запроса?

Заранее большое спасибо

РЕДАКТИРОВАТЬ:забыл конфигурацию nginx для настройки прокси-сервера-

    proxy_pass              http://10.10.1.57:9050;
    proxy_set_header        HTTP_X_GEOIP $http_x_geoip;
    proxy_set_header        GEOIP_COUNTRY_CODE $geoip_country_code;
    proxy_set_header        X-Real-IP $remote_addr;
    proxy_set_header        X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header        Host $http_host;
    proxy_set_header        Connection "";
    proxy_http_version      1.1;

ПРАВКА2:Принудительное закрытие Jetty соединения через request.getConnection().getEndPoint().close()ничего не дает -- очевидно, что соединение закрывается (как в LAST_ACK), но почему оно не проходит дальше этого? Nginx по какой-то причине держит соединение открытым к бэкенду?

решение1

Хорошо, мне наконец удалось убедить Джетти играть хорошо.

Подводя итог, вот как сейчас выглядят мои соединения на уровне всей машины:

24 LAST_ACK
36 CLOSING
117 FIN_WAIT_2
175 ESTABLISHED
351 FIN_WAIT_1
4725 TIME_WAIT

И между nginx и Jetty

1 FIN_WAIT_2
3 LISTEN
14 ESTABLISHED

Я уже закрывал все соединение и EndPoint(вызывая close()закрывает EndPointбазовый метод SocketChannel) использовал этот удобный метод:

private void finishRequest(String message, Request baseRequest, HttpServletResponse response) throws IOException {
    ByteArrayISO8859Writer writer = new ByteArrayISO8859Writer(1500);
    writer.write(message);
    writer.flush();

    // set the content length
    response.setContentLength(writer.size());

    // write the response
    OutputStream outputStream = response.getOutputStream();
    writer.writeTo(outputStream);

    // close the streams
    outputStream.close();
    writer.close();
    baseRequest.getConnection().getEndPoint().close();
}

(Я отправляю клиенту максимум одну строку, так что ничего особенного не требуется)

Однако это все равно привело к заполнению всего сервера LAST_ACK... Что заставило их окончательно исчезнуть, так это включение SO_LINGER(и принудительное немедленное закрытие сокета без тайм-аута)

connector.setSoLingerTime(0);

Связанный контент