Java.net.SocketInputStream.socketRead0中的线程卡住

2020-05-19 15:09:23

java.net.SocketInputStream.socketRead0()API做什么?为什么它经常出现在几个线程转储中?为什么会在像TastThread.io这样的线程转储分析工具中报告呢?是不是有什么我需要关心的事情?这个问题的潜在解决方案是什么?让我们找出这些问题的答案。

通过现实生活中的类比,记住新概念总是很容易的。假设你正在给你的妻子或女朋友打电话。一旦电话接通,如果她立即心情愉快,你会得到“你好,亲爱的(或亲爱的或甜心),你好吗?”:-)。如果你的电话在她工作的时候接通(比如她在办公室接孩子,健身房…)。她对说“你好,亲爱的(或亲爱的或甜心)”的回答可能会有延迟。…。.“。假设你的电话在她生气/心情不好的时候接通了,那么回应可能是不可预测的。只有上帝知道。您可能会在几秒钟/分钟后得到响应(甚至呼叫可能会被挂断:-)。因此,从连接调用到挂断调用这段时间基本上就是socketRead0()API。(感谢IBM的Douglas Spath提供了这个漂亮的示例来解释这个SocketRead0()API。)。

您的应用程序可能通过各种协议(如SOAP、REST、HTTP、HTTPS、jdbc、rmi…)与多个远程应用程序交互。所有连接都通过JDK java.net层执行较低的TCP-IP/套接字操作。在这一层,SocketInputStream.socketRead0()API用于读取和接收远程应用程序的数据。一些远程应用程序可能立即响应,一些应用程序可能需要一些时间才能响应,而一些应用程序可能根本不响应。在您的应用程序完全读取响应数据之前,您的应用程序线程将停留在此java.net.SocketInputStream.socketRead0()API中。

下面是一些示例堆栈跟踪,其中显示了“SocketInputStream.socketRead0”API中滞留的线程。您可以注意到,与SocketInputStream.socketRead0()API上停滞的协议线程无关。

";RMI TCP连接(2)-192.xxx.xx.xx";后台进程PRIO=6 tid=0x000000000a3e8800 nid=0x158e50 Runnable[0x000000000adbe000]java.lang.Thread.State:RUNNABLEat java.net.SocketInputStream.socketRead0(本地方法)at java.net.SocketInputStream.read(未知源)at java.net.SocketInputStream.read(未知源。.rmi.transport.tcp.TCPTransport$ConnectionHandler.run0(Unknown源)在sun.rmi.transport.tcp.TCPTransport$ConnectionHandler.run(Unknown源)在java.util.concurrent.ThreadPoolExecutor.runWorker(Unknown源)在java.util.concurrent.ThreadPoolExecutor$Worker.run(Unknown源)在java.lang.Thread.run(未知源)。

";线程-18";id=48 idx=0x9c tid=11696优先级=5活动,本机,守护进程jrockit/net/SocketNativeIO.readBytesPinned(Ljava/io/FileDescriptor;[BIII)I(Native方法),位于jrockit/net/SocketNativeIO.socketRead(SocketNativeIO.java:32)at java/net/SocketInputStream.socketRead0(Ljava/io/FileDescriptor;[biii)位于JJJROKIT/VM/RNI.c2java(java/net/SocketInputStream.read(SocketInputStream.java:129)at java/net/ManagedSocketInputStreamHighPerformanceNew.read(ManagedSocketInputStreamHighPerformanceNew.java:100)at java/net/SocketInputStream.read(SocketInputStream.java:182)at java/net/ManagedSocketInputStreamHighPerformanceNew.read(ManagedSocketInputStreamHighPerformanceNew.java:55)at oracle/ons/InputBuffer.getNextString(InputBuffer.java:137)at oracle/ons/ReceiverThread.run(ReceiverThread.java:295)at JJJJ)V(原生方法)的I(SocketInputStream.java)。

";AMQP连接192.xx.xxx.xxx:5672";优先级=5 RUNNABLEjava.net.SocketInputStream.socketRead0(Native Method)java.net.SocketInputStream.socketRead(SocketInputStream.java:116)java.net.SocketInputStream.read(SocketInputStream.java:170)java.net.SocketInputStream.read(SocketInputStream.java:141)java.io.BufferedInputStream.fill(BufferedInputStream.java:246)java.io.BufferedInputStream.read(BufferedInputStream.java:265)java.io.DataInputStream.readUnsignedByte(DataInputStream.java:288)com.rabbitmq.client.impl.Frame.readFrom(Frame.java:95)com.rabbitmq.client.。impl.SocketFrameHandler.readFrame(SocketFrameHandler.java:139)com.rabbitmq.client.impl.AMQConnection$MainLoop.run(AMQConnection.java:536)java.lang.Thread.run(Thread.java:745)。

";线程-2012";ID=218 IDX=0x09c TID=196 PRIO=10活动,本机,java.net.SocketInputStream.read(SocketInputStream.java:140)at com.ibm.db2.jcc.t4.z.b(z.java:199)at com.ibm.db2.jcc.t4.z.c(z.java:289)at com.ibm.db2.jcc.t4.z.c(z.java:402)at com.ibm.db2.jcc.t4.z.c(z.java:402)

如果线程在SocketInputStream.socketRead0 API中停滞,并且在较长时间内无法恢复,则发起事务的客户将不会在其屏幕上看到任何响应。它可以迷惑用户,迷惑用户。如果多个线程卡在SocketInputStream.socketRead0API中,并且在较长时间内不能恢复,可能会给您的应用程序带来严重的可用性问题。

大多数应用程序没有设置适当的超时设置来从SocketInputStream.socketRead0恢复,因此它们最终会长时间停留在此API中。设置适当的超时是每个应用程序都应该执行的一种很好的自我防御机制。以下是您可以应用于您的应用程序的几个超时设置,因为您可能会看到合适的设置:

您可以传递这两个功能强大的超时网络属性,它们可以全局适用于使用java.net.URLConnection的所有协议处理程序:

sun.net.client.defaultConnectTimeout指定与主机建立连接的超时时间(以毫秒为单位)。例如,对于HTTP连接,它是建立到HTTP服务器的连接时的超时。对于ftp连接,它是建立到ftp服务器的连接时的超时。

defaultReadTimeout指定在与资源建立连接时从输入流读取时的超时时间(以毫秒为单位)。

如果您直接使用Sockets编程,可以考虑通过调用setSoTimeout()API来设置套接字的超时。

此接口可以传递以毫秒为单位的超时值。如果远程应用在指定的超时时间内没有响应,将抛出java.net.SocketTimeoutException。此异常将释放线程,允许它在其他调用上工作。注意:如果超时值被传递为0,那么它将被解释为无限超时,这意味着线程永远不会超时。

如果使用JDBC(Java数据库连接)进行连接,可以考虑使用setQueryTimeout()API设置超时值。

此API将设置JDBC驱动程序从数据库获取结果将等待的秒数。如果超过限制,则抛出SQLTimeoutException。JDBC驱动程序将此限制应用于Execute、ecuteQuery()和ecuteUpdate()方法。默认情况下,对运行语句完成的时间没有限制。

如果您正在连接Oracle数据库,并且看到SocketInputStream.socketRead0()API上滞留了大量线程,则可以考虑传递-D oracle.jdbc.ReadTimeout系统属性。

您需要在应用程序启动期间传递上述参数。值需要以毫秒为单位指定。

如果您的应用程序恰好在IBM Websphere上运行,您可以考虑设置以下属性:

b.第二个属性syncQueryTimeoutWithTransactionTimeout也可以设置为数据源自定义属性。使用此设置,WebSphere将计算事务超时之前的剩余时间(如果在全局事务中运行),并自动将查询超时设置为此值。

您还可以在为Web服务客户端设置的HTTP传输策略中设置“readTimeout”属性,或者在应用程序代码中的org.apache.axis2.context.MessageContext上设置“timeout”。

未从SocketInputStream.socketRead0 API恢复的线程也可能因网络连接或负载均衡器问题而产生。我们在过去已经看到,有时远程应用程序可能不会发出适当的ACK或FIN数据包。您可能需要联系网络工程师或云托管提供商支持团队来解决问题。

在您的端,您可以使用诸如Wireshark之类的TCP/IP跟踪工具来查看您和远程应用程序之间在网络中发送的数据包。它可以帮助您缩小范围,无论问题是在您的网络一端还是在网络的另一端。

有时,事务很可能因为远程应用程序中的性能问题而变慢。在这些情况下,您需要让远程应用程序意识到速度变慢,并与他们一起解决问题。

您还可以考虑使用非阻塞HTTP客户端库,如Grizzly或Netty,它们没有阻塞操作来挂起线程。但是这个解决方案更多的是一个战略性的解决方案,它涉及代码更改和彻底的测试。

请注意,这是一个全面的列表,但可能不是潜在解决方案的完整列表。如果您有其他解决方案和超时设置要添加到此博客中,请在下面的反馈部分给我们留言。我们将很高兴用您的推荐来更新这个博客。