Почему я вижу много сокетов в состоянии 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

Это, вероятно, означает, что вы не очищаете свои входящие соединения. Убедитесь, что сокеты закрываются в конце каждой транзакции. (Лучше всего сделать окончательно заблокировать рядом с началом вашего кода сервера, чтобы соединения закрывались, даже если произошли исключения на стороне сервера.)