17

クライアント サーバー アプリ、1 サーバー、約 10 クライアントがあります。これらは、カスタム クエリを使用して tcp ソケット経由で通信します。

システムは何ヶ月もスムーズに動作していましたが、ある時点で、毎日スケジュールされたサーバー FULL GC に約 50 秒かかった後、クライアントから送信されたクエリとサーバーから受信した応答の間の時間が長いことがわかりました。 > 10~20代。約 3 時間後にシステムが回復し、すべてが正常に動作するようになりました。

この問題を調査したところ、次のことがわかりました。

  1. クライアントとサーバーの両方でガベージ コレクションの問題がない
  2. サーバーでのクエリ処理時間はわずかでした。
  3. サーバーの負荷が高かった。
  4. ネットワーク帯域幅は飽和状態ではありませんでした。
  5. FULL GC 中に接続がリセットされませんでした (毎日の FULL GC はそれまで通常のイベントでした)。
  6. 最近、マシンと OS が Centos 6 (カーネル 2.6.32) から Centos 7 (カーネル 3.10.0) に変更されましたが、新しい構成は広範囲にテストされています。また、Oracle JDK のバージョンが 1.7.65 から 1.7.75 に変更されました。

サーバーでスレッド ダンプを取得しました。

java.lang.Thread.State: RUNNABLE
    at java.io.FilterInputStream.read(FilterInputStream.java:83)
    at util.network.BytesBasedSocketConnection$ReadConnectionRunnable.run(BytesBasedSocketConnection.java:293)
    at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:471)
    at java.util.concurrent.FutureTask.run(FutureTask.java:262)
    at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.access$201(ScheduledThreadPoolExecutor.java:178)
    at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:292)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615)
    at java.lang.Thread.run(Thread.java:745)

FilterInputStream.read()次のとおりです。

    public int read() throws IOException {
    return in.read();
}

in私たちのコードの はですBufferedInputStream

問題は、フル GC の一時停止後にほとんどの接続が遅くなったのはなぜですか? スタックトレースが で終わるのはなぜFilterInputStream.read()ですか? ソケット入力ストリームまたはソケット入力ストリームのどこかで終了するべきではありませんBufferedInputStreamか? この読み取りにより、サーバーの負荷が高くなる可能性はありますか?

読み取りに使用するコード:

int constructLength = _socketDIS.readInt();
ByteArrayOutputStream constructBOAS = new ByteArrayOutputStream(constructLength);
for (int i = 0; i != constructLength; i++)
      constructBOAS.write(_socketDIS.read());
constructBOAS.close();
byte[] bytes = constructBOAS.toByteArray();

どこ:

_socketDIS = new DataInputStream(new BufferedInputStream(_socket.getInputStream()));

正常に動作しているクライアント接続からのスタック トレースを次に示します。

java.lang.Thread.State: RUNNABLE
    at java.net.SocketInputStream.socketRead0(Native Method)
    at java.net.SocketInputStream.read(SocketInputStream.java:152)
    at java.net.SocketInputStream.read(SocketInputStream.java:122)
    at java.io.BufferedInputStream.fill(BufferedInputStream.java:235)
    at java.io.BufferedInputStream.read(BufferedInputStream.java:254)
    - locked <0x00007f522cbebca8> (a java.io.BufferedInputStream)
    at java.io.DataInputStream.readInt(DataInputStream.java:387)
    at util.network.BytesBasedSocketConnection$ReadConnectionRunnable.run(BytesBasedSocketConnection.java:287)
    at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:471)
    at java.util.concurrent.FutureTask.run(FutureTask.java:262)
    at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.access$201(ScheduledThreadPoolExecutor.java:178)
    at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:292)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615)
    at java.lang.Thread.run(Thread.java:745)

アップデート:

EJPの回答について:

  1. EOS は関与しておらず、接続は確立されていましたが、非常に遅かったです。

  2. EOS があったとしても、コードが EOS でどのように回転するかはわかりません。値forによって制限されconstructLengthます。それでも、提案された改善は有効です。

  3. 問題のあるスタックトレースは、から継承されたDataInputStream( ) で行われた読み取りで終了します。上記のコードを参照してください。、ないは欠落しています。ここには a が呼び出されており、これには独自のメソッドが定義されています。しかし、スタック トレースは途中で停止し、到達していません。なんで?(_socketDIS.read()FilterInputStream.read()DataInputStreamBufferedInputStreamread()FilterInputStream.read()in.read()BufferedInputStreamread()BufferedInputStream.read()

4

4 に答える 4

7

一度に 1 バイトずつ読み取ると、CPU が無駄になります。これを捨てる:

int constructLength = _socketDIS.readInt();
ByteArrayOutputStream constructBOAS = new ByteArrayOutputStream(constructLength);
for (int i = 0; i != constructLength; i++)
      constructBOAS.write(_socketDIS.read());
constructBOAS.close();
byte[] bytes = constructBOAS.toByteArray();

これを使用します:

int constructLength = _socketDIS.readInt();
byte[] bytes = new byte[constructLength];
_socketDIS.readFully(bytes);

NB_socketDISは明らかにaではなく、バッファリングされていないaです。BufferedInputStreamDataInputStream,

編集

スタックトレースが FilterInputStream.read() で終わるのはなぜですか?

よく見る。BufferedInputStream3 つの read() オーバーロードすべてを実装しているわけではありません。そのうちの 1 つは、基本クラスである FilterInputStream に実装されており、他の 2 つのオーバーロードはそれを呼び出します。

BufferedInputStream のどこかで終了するべきではありませんか

いいえ、上記を参照してください。

またはソケット入力ストリームで?

はい、ブロックしていた場合はそうではありません。おそらく、ストリームの最後でスピンしているため、コードが貧弱なためです。

この読み取りにより、サーバーの負荷が高くなる可能性はありますか?

はい。

于 2015-05-20T12:24:04.537 に答える
-1

エラーなしでストリームを読み書きしたり、接続を遅くしたりするには、フラッシュを試みる必要があると思います。

于 2015-05-28T22:44:38.757 に答える