在Full GC之后导致套接字连接变慢的原因是什么?

dce*_*chi 18 java sockets networking high-load inputstream

我们有一个客户端服务器应用,1个服务器,大约10个客户端.它们使用自定义查询通过tcp套接字进行通信.

该系统运行平稳了好几个月,但在某些时候,在每日计划服务器FULL GC耗时约50秒后,我们发现客户端发送的查询与从服务器收到的响应之间的时间很长,> 10-20s.系统恢复3个小时后,一切都恢复正常.

在调查问题时,我们发现:

  1. 客户端和服务器上都没有垃圾回收问题
  2. 服务器上的查询处理时间很短.
  3. 服务器上的负载很高.
  4. 网络带宽未饱和.
  5. FULL GC期间未重置连接(每日FULL GC是正常事件,直到那时)
  6. 机器和操作系统最近从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)
Run Code Online (Sandbox Code Playgroud)

FilterInputStream.read()如下:

    public int read() throws IOException {
    return in.read();
}
Run Code Online (Sandbox Code Playgroud)

in我们的代码是一个BufferedInputStream.

问题是:为什么大多数连接在完全GC暂停后放慢了?为什么stacktrace结束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();
Run Code Online (Sandbox Code Playgroud)

哪里:

_socketDIS = new DataInputStream(new BufferedInputStream(_socket.getInputStream()));
Run Code Online (Sandbox Code Playgroud)

这是来自运行良好的客户端连接的堆栈跟踪:

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)
Run Code Online (Sandbox Code Playgroud)

更新:

关于EJP答案:

  1. 没有涉及EOS,连接已经开始,但它们非常慢

  2. 即使有一个EOS,我也看不出代码如何在EOS中旋转,for这个constructLength值受到了限制.但是,建议的改进仍然有效.

  3. 带有问题的堆栈跟踪以对继承的DataInputStream((_socketDIS.read())的读取结束FilterInputStream.read(),请参阅上面的代码.DataInputStream,不是BufferedInputStream错过了read().这里FilterInputStream.read()有一个in.read()叫做a BufferedInputStream,这个有自己read()定义的方法.但堆栈跟踪在中间停止,没有到达BufferedInputStream.read().为什么?

use*_*421 7

一次读取一个字节会浪费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();
Run Code Online (Sandbox Code Playgroud)

并使用这个:

int constructLength = _socketDIS.readInt();
byte[] bytes = new byte[constructLength];
_socketDIS.readFully(bytes);
Run Code Online (Sandbox Code Playgroud)

NB _socketDIS显然不是一个,BufferedInputStream而是一个DataInputStream,无缓冲的.

编辑

为什么stacktrace在FilterInputStream.read()中结束?

仔细看.BufferedInputStream没有实现所有三个read()重载.其中一个,我忘了哪个,在FilterInputStream中实现,基类,以及其他两个重载调用它.

它不应该在BufferedInputStream中的某个地方结束

不,见上文.

或者在套接字输入流中?

是的,如果它是阻塞的,但事实并非如此,可能是因为你在流的末尾旋转,因为你的代码很差.

这读取会导致服务器上的高负载吗?

是.


Old*_*eon 3

堆栈跟踪显示您正在使用ScheduledThreadPoolExecutor. 我建议你研究一下时间表。延迟很可能只是因为阅读是按某种时间表进行的——这对我来说似乎很愚蠢。