Почему я вижу много сокетов в состоянии CLOSE_WAIT, когда webservice перестает работать?
Мой веб-сервис java, работающий на Jetty, падает через несколько часов, и исследование показывает, что многие сокеты находятся в состоянии CLOSE_WAIT. Пока он работает нормально, похоже, что нет сокетов в состоянии CLOSE_WAIT, но когда он идет не так, есть нагрузки.
Я нашел это определение
CLOSE-WAIT: локальная конечная точка получила запрос о завершении соединения и подтвердила его, например. было выполнено пассивное закрытие, и локальной конечной точке необходимо выполнить активное закрытие, чтобы покинуть это состояние.
С netstat на моем сервере я вижу список сокетов tcp в состоянии CLOSE_WAIT, локальный адрес - мой сервер, а внешний адрес - мой балансировщик нагрузки. Поэтому я предполагаю, что это означает, что клиент (балансировщик нагрузки) только что завершил соединение на своем конце некорректным образом, и мой сервер неправильно закрыл соединение в конце.
Но как это сделать, мой код Java не касается сокетов низкого уровня?
Или это прекращение соединения балансировки нагрузки из-за более ранней проблемы, вызванной тем, что мой сервер делает неправильный код.
Ответы
Ответ 1
Звучит как ошибка в Jetty или JVM, возможно, это обходное решение будет работать для вас:
http://www.tux.hk/index.php?entry=entry090521-111844
Добавьте следующие строки в /etc/sysctl.conf
net.ipv4.tcp_fin_timeout = 30
net.ipv4.tcp_keepalive_intvl = 2
net.ipv4.tcp_keepalive_probes = 2
net.ipv4.tcp_keepalive_time = 1800
И затем выполните
sysctl -p
или выполните перезагрузку
Ответ 2
У нас та же проблема в нашем проекте. Я не уверен, что это ваше дело, но, возможно, это будет полезно.
Причина заключалась в том, что огромное количество запросов обрабатывалось бизнес-логикой с синхронизированным блоком. Поэтому, когда клиент отправил пакеты для удаления соединения, поток, связанный с этим сокетом, был занят, ожидая мониторинга.
В журналах отображаются исключения для org.eclipse.jetty.io.WriteFlusher при методе записи:
DEBUG org.eclipse.jetty.io.WriteFlusher - write - write exception
org.eclipse.jetty.io.EofException: null
at org.eclipse.jetty.io.ChannelEndPoint.flush
(ChannelEndPoint.java:192) ~[jetty-io-9.2.10.v20150310.jar:9.2.10.v20150310]
и для org.eclipse.jetty.server.HttpOutput при закрытом методе. Я думаю, что исключение на закрытом шаге является причиной состояния CLOSE_WAIT сокетов:
DEBUG org.eclipse.jetty.server.HttpOutput - close -
org.eclipse.jetty.io.EofException: null
at org.eclipse.jetty.server.HttpConnection$SendCallback.reset
(HttpConnection.java:622) ~[jetty-server-9.2.10.v20150310.jar:9.2.10.v20150310]
Быстрое решение в нашем случае состояло в том, чтобы увеличить idleTimeout. Правильное решение (опять же в нашем случае) - рефакторинг кода.
Поэтому мой совет - внимательно прочитать журналы Jetty с уровнем DEBUG, чтобы найти исключения и проанализировать производительность приложения с помощью VisualVM. Может быть, причиной является узкое место производительности (синхронизированные блоки?).
Ответ 3
Я подозреваю, что это может быть причиной длительного или бесконечного цикла/бесконечного ожидания в вашем серверном коде, и Jetty просто никогда не получит шанс закрыть соединение (если нет какого-то тайм-аута, который принудительно закрывает сокет через определенный период). Рассмотрим следующий пример:
public class TestSocketClosedWaitState
{
private static class SocketResponder implements Runnable
{
private final Socket socket;
//Using static variable to control the infinite/waiting loop for testing purposes, with while(true) Eclipse would complain of dead code in writer.close() -line
private static boolean infinite = true;
public SocketResponder(Socket socket)
{
this.socket = socket;
}
@Override
public void run()
{
try
{
PrintWriter writer = new PrintWriter(socket.getOutputStream());
writer.write("Hello");
//Simulating slow response/getting stuck in an infinite loop/waiting something that never happens etc.
do
{
Thread.sleep(5000);
}
while(infinite);
writer.close(); //The socket will stay in CLOSE_WAIT from server side until this line is reached
}
catch(Exception e)
{
e.printStackTrace();
}
System.out.println("DONE");
}
}
public static void main(String[] args) throws IOException
{
ServerSocket serverSocket = new ServerSocket(12345);
while(true)
{
Socket socket = serverSocket.accept();
Thread t = new Thread(new SocketResponder(socket));
t.start();
}
}
}
Если параметр infinite
-variable равен true, Printwriter (и базовый сокет) никогда не закрывается из-за бесконечного цикла. Если я запустил это и подключился к сокету с помощью telnet, то выйдите из telnet-клиента, netstat
покажет серверный сокет еще в CLOSE_WAIT
-state (я также мог видеть клиентский сокет в состоянии FIN_WAIT2 какое-то время, но оно исчезнет):
~$ netstat -anp | grep 12345
tcp6 0 0 :::12345 :::* LISTEN 6460/java
tcp6 1 0 ::1:12345 ::1:34606 CLOSE_WAIT 6460/java
Сертифицированный сокет на стороне сервера застревает в состоянии CLOSE_WAIT. Если я проверю стеки потоков для процесса, я вижу поток, ожидающий внутри do... while -loop:
~$ jstack 6460
<OTHER THREADS>
"Thread-0" prio=10 tid=0x00007f424013d800 nid=0x194f waiting on condition [0x00007f423c50e000]
java.lang.Thread.State: TIMED_WAITING (sleeping)
at java.lang.Thread.sleep(Native Method)
at TestSocketClosedWaitState$SocketResponder.run(TestSocketClosedWaitState.java:32)
at java.lang.Thread.run(Thread.java:701)
<OTHER THREADS...>
Если я установил значение infinite
-variable в false и сделаю то же самое (подключи клиенту и отключитесь), сокет с CLOSE_WAIT
-state покажет, пока автор не будет закрыт (закрытие базового сокета), а затем исчезает. Если автор или сокет никогда не закрыты, серверный сокет снова застрянет в CLOSED_WAIT
, даже если поток завершится (я не думаю, что это должно произойти в Jetty, если ваш метод вернется в какой-то момент, возможно, Jetty должен заботиться о закрытии гнезда).
Итак, шаги, которые я предлагаю вам попробовать, чтобы найти виновника,
- Добавить регистрацию в свои методы, чтобы увидеть, где они идут/что они делают.
- Проверьте свой код, есть ли там места, где выполнение может застрять в бесконечном цикле или занять очень долгое время, предотвращая закрытие базового сокета?
- Если это все еще происходит, возьмите дамп потока из работающего процесса Jetty с помощью
jstack
при следующей ошибке и попытайтесь определить любые "застрявшие" потоки
- Есть ли что-то, что-то может что-то бросить (OutOfMemoryError или такое), которое может не попасть в основную Jetty-архитектуру, вызывающую ваш метод? Я никогда не заглядывал внутрь внутренних элементов Jetty, вполне возможно, что он ловит
Throwable
s, поэтому это, вероятно, не проблема, но, возможно, стоит проверить, не сработает ли еще что-либо.
Вы также можете назвать потоки, когда они вводят и выходят из ваших методов с помощью
String originalName = Thread.currentThread().getName();
Thread.currentThread().setName("myMethod");
//Your code...
Thread.currentThread().setName(originalName);
чтобы обнаружить их легче, если есть много потоков.
Ответ 4
У меня возникла аналогичная проблема, в то время как код виновника может отличаться, симптомы были
1) Сервер (Jetty) запускал еще не запрос обработки
2) Никаких дополнительных нагрузок/исключений не было
3) Было слишком много соединений CLOSE_WAIT.
Они предположили, что все рабочие потоки на сервере где-то застряли. Jstack Thread dump показал, что все наши рабочие потоки застряли в объекте HttpClient apache. (из-за незакрытых объектов ответа), и поскольку все потоки ожидали бесконечно, ни один из них не был доступен для обработки входящего запроса.
Ответ 5
Остается ли балансировка нагрузки? Попробуйте остановить балансировку нагрузки и посмотреть, не является ли это проблемой не сервер.
Ответ 6
Это, вероятно, означает, что вы не очищаете свои входящие соединения. Убедитесь, что сокеты закрываются в конце каждой транзакции. (Лучше всего сделать окончательно заблокировать рядом с началом вашего кода сервера, чтобы соединения закрывались, даже если произошли исключения на стороне сервера.)