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

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

Jetty получает свыше 500 запросов в секунду постоянно, и в последнее время возникла проблема, из-за которой у меня постоянно будет более 60000 соединений в состоянии 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 -> причальных соединений

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..
  • На конце Пристани я поставил 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;

EDIT2: Принудительно 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);
Другие вопросы по тегам