У меня есть машина 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);