My Java Jettyで実行されているWebサービスは数時間後に転倒し、調査によりCLOSE_WAITステータスの多くのソケットが示されます。負荷があるとうまくいかない。
私はこれを見つけました 定義
CLOSE-WAIT:ローカルエンドポイントが接続終了要求を受信し、確認しました。パッシブクローズが実行されており、ローカルエンドポイントはアクティブクローズを実行してこの状態を終了する必要があります。
サーバーのnetstatを使用すると、CLOSE_WAITステータスのtcpソケットのリストが表示されます。ローカルアドレスはサーバーであり、外部アドレスはロードバランサーマシンです。したがって、これは、クライアント(ロードバランサー)が何らかの不適切な方法でその端で接続を終了したばかりであり、サーバーがその端で接続を適切に閉じなかったことを意味します。
しかし、どうすればそれを行うことができますか?Javaコードは低レベルのソケットを処理しませんか?
または、ロードバランサーが接続を終了しているのは、サーバーがコード内で間違っているために発生した初期の問題が原因です。
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
または再起動する
プロジェクトにも同じ問題があります。これがあなたの場合であるかどうかはわかりませんが、おそらく役立つでしょう。
理由は、同期ブロックを使用したビジネスロジックによって膨大な数のリクエストが処理されたためです。したがって、クライアントがパケットを送信して接続を切断すると、このソケットにバインドされたスレッドはビジーであり、モニターを待機していました。
ログには、書き込みメソッドで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でアプリケーションのパフォーマンスを分析することです。たぶん、その理由はパフォーマンスのボトルネック(ブロックの同期?)です。
これは、サーバーコードで長時間または無限ループ/無限待機を引き起こすものである可能性があり、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がソケットのクローズを処理する必要があります)。
だから、犯人を見つけて見つけるように勧める手順は
jstack
でスレッドダンプを取得し、「スタック」スレッドを特定してください。Throwable
sをキャッチしている可能性が高いため、これはおそらく問題ではありませんが、他のすべてが失敗した場合はチェックする価値がありますまた、スレッドがメソッドに入ったり出たりするときにスレッドに名前を付けることもできます
String originalName = Thread.currentThread().getName();
Thread.currentThread().setName("myMethod");
//Your code...
Thread.currentThread().setName(originalName);
多数のスレッドが実行されている場合、それらを簡単に見つけることができます。
私は同様の問題に直面しましたが、原因コードは異なる可能性がありますが、症状は1)サーバー(Jetty)が実行されていてリクエストを処理していない2)余分な通常の負荷/例外がなかった3)CLOSE_WAIT接続が多すぎる.
これらは、サーバー内のすべてのワーカースレッドがどこかでスタックしていることを示唆しています。 Jstackスレッドダンプは、すべてのワーカースレッドがApache HttpClientオブジェクトでスタックしていることを示しました。 (閉じられていない応答オブジェクトのため)、およびすべてのスレッドが無限に待機していたため、着信要求を処理するために利用できるものはありませんでした。