web-dev-qa-db-ja.com

Webサービスの動作が停止したときに、CLOSE_WAITステータスの多数のソケットが表示されるのはなぜですか?

My Java Jettyで実行されているWebサービスは数時間後に転倒し、調査によりCLOSE_WAITステータスの多くのソケットが示されます。負荷があるとうまくいかない。

私はこれを見つけました 定義

CLOSE-WAIT:ローカルエンドポイントが接続終了要求を受信し、確認しました。パッシブクローズが実行されており、ローカルエンドポイントはアクティブクローズを実行してこの状態を終了する必要があります。

サーバーのnetstatを使用すると、CLOSE_WAITステータスのtcpソケットのリストが表示されます。ローカルアドレスはサーバーであり、外部アドレスはロードバランサーマシンです。したがって、これは、クライアント(ロードバランサー)が何らかの不適切な方法でその端で接続を終了したばかりであり、サーバーがその端で接続を適切に閉じなかったことを意味します。

しかし、どうすればそれを行うことができますか?Javaコードは低レベルのソケットを処理しませんか?

または、ロードバランサーが接続を終了しているのは、サーバーがコード内で間違っているために発生した初期の問題が原因です。

16
Paul Taylor

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

または再起動する

6
Eirenliel

プロジェクトにも同じ問題があります。これがあなたの場合であるかどうかはわかりませんが、おそらく役立つでしょう。

理由は、同期ブロックを使用したビジネスロジックによって膨大な数のリクエストが処理されたためです。したがって、クライアントがパケットを送信して接続を切断すると、このソケットにバインドされたスレッドはビジーであり、モニターを待機していました。

ログには、書き込みメソッドで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メソッド。私は、クローズステップでの例外がソケットの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でアプリケーションのパフォーマンスを分析することです。たぶん、その理由はパフォーマンスのボトルネック(ブロックの同期?)です。

5
Vitalii Ivanov

これは、サーバーコードで長時間または無限ループ/無限待機を引き起こすものである可能性があり、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クライアントを終了すると、netstatCLOSE_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でスレッドダンプを取得し、「スタック」スレッドを特定してください。
  • メソッドを呼び出す基盤となるJettyアーキテクチャに捕捉されない何か(OutOfMemoryErrorなど)がスローされる可能性はありますか? Jettyの内部を覗いたことがないので、Throwablesをキャッチしている可能性が高いため、これはおそらく問題ではありませんが、他のすべてが失敗した場合はチェックする価値があります

また、スレッドがメソッドに入ったり出たりするときにスレッドに名前を付けることもできます

        String originalName = Thread.currentThread().getName();
        Thread.currentThread().setName("myMethod");

        //Your code...

        Thread.currentThread().setName(originalName);

多数のスレッドが実行されている場合、それらを簡単に見つけることができます。

4
esaj

私は同様の問題に直面しましたが、原因コードは異なる可能性がありますが、症状は1)サーバー(Jetty)が実行されていてリクエストを処理していない2)余分な通常の負荷/例外がなかった3)CLOSE_WAIT接続が多すぎる.

これらは、サーバー内のすべてのワーカースレッドがどこかでスタックしていることを示唆しています。 Jstackスレッドダンプは、すべてのワーカースレッドがApache HttpClientオブジェクトでスタックしていることを示しました。 (閉じられていない応答オブジェクトのため)、およびすべてのスレッドが無限に待機していたため、着信要求を処理するために利用できるものはありませんでした。

0
Abhishek Gupta