通过现实的类比记住新概念总是很容易的,所以让我们看看是否可以通过这样的例子更好地理解这个Java问题。 假设您正在打电话给妻子或女友。 接通电话后,如果她心情愉快,您会立即收到诸如“你好亲爱的(或亲爱的或亲爱的,或彼此的宠物名字!),你好吗?”之类的回复。
如果您在通话过程中打了电话,则响应可能会延迟。 现在想象一下,当她心情不好时,您的电话接通了。 这样,响应可能是不可预测的。 只有上帝知道。 几秒钟/分钟后,您可能会收到响应。 因此,从调用建立连接到挂断电话为止的等待时间基本上是socketRead0()API。 (特别感谢IBM的Douglas Spath提供了这个漂亮的示例来说明此SocketRead0()API。)
您的应用程序可能通过各种协议(例如SOAP,REST,HTTP,HTTPS,JDBC,RMI等)与多个远程应用程序接口,所有连接都通过JDK java.net层执行较低的TCP-IP / Socket操作。 在此层中,使用SocketInputStream.socketRead0()API读取和接收远程应用程序中的数据。 一些远程应用程序可能会立即响应,一些可能需要一些时间才能响应,某些应用程序可能根本不响应。 在您的应用程序完全读取响应数据之前,您的应用程序线程将停留在此java.net.SocketInputStream.socketRead0()API中。
示例线程转储堆栈跟踪
以下是一些示例堆栈跟踪,这些示例显示了卡在“ SocketInputStream.socketRead0” API中的线程。 您可以注意到,无论协议线程被卡在SocketInputStream.socketRead0()API上。
卡在SocketInputStream.socketRead0()API中的RMI线程:
"RMI TCP Connection(2)-192.xxx.xx.xx" daemon prio=6 tid=0x000000000a3e8800 nid=0x158e50 runnable [0x000000000adbe000]
java.lang.Thread.State: RUNNABLE
at java.net.SocketInputStream.socketRead0(Native Method)
at java.net.SocketInputStream.read(Unknown Source)
at java.net.SocketInputStream.read(Unknown Source)
at java.io.BufferedInputStream.fill(Unknown Source)
at java.io.BufferedInputStream.read(Unknown Source)
- locked (0x00000007ad784010) (a java.io.BufferedInputStream)
at java.io.FilterInputStream.read(Unknown Source)
at sun.rmi.transport.tcp.TCPTransport.handleMessages(Unknown Source)
at sun.rmi.transport.tcp.TCPTransport$ConnectionHandler.run0(Unknown Source)
at sun.rmi.transport.tcp.TCPTransport$ConnectionHandler.run(Unknown Source)
at java.util.concurrent.ThreadPoolExecutor.runWorker(Unknown Source)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(Unknown Source)
at java.lang.Thread.run(Unknown Source)
Oracle数据库连接卡在SocketInputStream.socketRead0()API中:
"Thread-18" id=48 idx=0x9c tid=11696 prio=5 alive, in native, daemon
at jrockit/net/SocketNativeIO.readBytesPinned(Ljava/io/FileDescriptor;[BIII)I(Native Method)
at jrockit/net/SocketNativeIO.socketRead(SocketNativeIO.java:32)
at java/net/SocketInputStream.socketRead0(Ljava/io/FileDescriptor;[BIII)I(SocketInputStream.java)
at 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 jrockit/vm/RNI.c2java(JJJJJ)V(Native Method)
RabbitMQ卡在SocketInputStream.socketRead0()API中:
"AMQP Connection 192.xx.xxx.xxx:5672" prio=5 RUNNABLE
java.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)
IBM DB2语句执行卡在SocketInputStream.socketRead0()API中:
"Thread-2012" id=218 idx=0x09c tid=196 prio=10 alive, in native, daemon
java.lang.Thread.State: RUNNABLE
at java.net.SocketInputStream.socketRead0(Native Method)
at 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.v(z.java:1170)
at com.ibm.db2.jcc.t4.cb.b(cb.java:40)
at com.ibm.db2.jcc.t4.q.a(q.java:32)
at com.ibm.db2.jcc.t4.sb.i(sb.java:135)
at com.ibm.db2.jcc.am.yn.gb(yn.java:2066)
at com.ibm.db2.jcc.am.zn.pc(zn.java:3446)
at com.ibm.db2.jcc.am.zn.b(zn.java:4236)
at com.ibm.db2.jcc.am.zn.fc(zn.java:2670)
at com.ibm.db2.jcc.am.zn.execute(zn.java:2654)
at com.ibm.ws.rsadapter.jdbc.WSJdbcPreparedStatement.execute(WSJdbcPreparedStatement.java:618)
at com.mycompany.myapp.MyClass.executeDatabaseQuery(MyClass.java:123)
解决方案
如果您的线程卡在SocketInputStream.socketRead0 API中,并且长时间无法从中恢复,那么发起事务的客户将不会在其屏幕上看到任何响应。 它会引起困惑,使用户感到困惑。 如果多个线程卡在SocketInputStream.socketRead0 API中,并且长时间无法恢复,则可能对您的应用程序造成严重的可用性问题。
在此,我们概述了解决此问题的一些潜在解决方案:
- 1.仪器超时设置
- 1.1。 JVM网络设置
- 1.2。 setSoTimeout
- 1.3。 JDBC
- 1.4。 Oracle JDBC
- 1.5。 网络圈
- 1.6。 轴2
- 2.验证网络连接
- 3.使用远程应用程序
- 4.非阻塞HTTP客户端
1.仪器超时设置
大多数应用程序未设置适当的超时设置以从SocketInputStream.socketRead0中恢复,因此它们最终会长时间卡在此API中。 设置适当的超时是每个应用程序都应该采取的一种强大的自我防御机制。 您可以根据自己的需要将一些超时设置应用于您的应用程序:
JVM网络设置
您可以传递以下两个强大的超时网络属性,这些属性可以全局适用于所有使用java.net.URLConnection的协议处理程序:
-Dsun.net.client.defaultConnectTimeout
-Dsun.net.client.defaultReadTimeout
sun.net.client.defaultConnectTimeout
指定建立到主机的连接的超时(以毫秒为单位)。 例如,对于http连接,这是建立与http服务器的连接时的超时。 对于ftp连接,这是建立与ftp服务器的连接时的超时。
当建立与资源的连接时, sun.net.client.defaultReadTimeout
指定从输入流读取时的超时(以毫秒为单位)。
可以在此处找到有关JVM网络设置的更多详细信息。
setSoTimeout
如果直接使用Socket进行编程,则可以考虑通过调用setSoTimeout()API在套接字上设置超时。
您可以以毫秒为单位向该API传递超时值。 如果远程应用程序在指定的超时时间内未响应,则将抛出java.net.SocketTimeoutException。 此异常将释放线程,使其可以在其他调用上工作。 注意:如果将超时值传递为0,则将其解释为无限超时,这意味着线程将永远不会超时。
JDBC
如果使用JDBC(Java数据库连接)进行连接,则可以考虑使用setQueryTimeout()API设置超时值。
该API将设置JDBC驱动程序等待从数据库获取结果的秒数。 如果超出限制,则抛出SQLTimeoutException。 JDBC驱动程序将此限制应用于execute,executeQuery()和executeUpdate()方法。 默认情况下,对运行中的语句完成的时间没有限制。
Oracle JDBC
如果您正在连接Oracle数据库,并且看到SocketInputStream.socketRead0() API
阻塞了许多线程,则可以考虑传递-Doracle.jdbc.ReadTimeout系统属性。
您需要在应用程序启动期间传递上述参数。 需要以毫秒为单位指定值。
网络圈
如果您的应用程序恰好在IBM Websphere上运行,则可以考虑设置以下属性:
- 管理员可以设置
webSphereDefaultQueryTimeout
数据源定制属性。
第二个属性syncQueryTimeoutWithTransactionTimeout
,也可以设置为数据源自定义属性。 使用此设置,WebSphere将计算事务超时(如果在全局事务中运行)之前的剩余时间,并将查询超时自动设置为该值。
轴2
您还可以在Web服务客户端的HTTP传输策略集中设置“ readTimeout”属性,或者在应用程序代码中的org.apache.axis2.context.MessageContext上设置“ timeout”。
2.验证网络连接
由于网络连接或负载平衡器方面的问题,无法从SocketInputStream.socketRead0 API恢复的线程也会产生。 过去我们已经看到,有时远程应用程序可能不会发出适当的ACK或FIN数据包。 您可能需要聘请网络工程师或云托管提供商支持团队来解决此问题。
最后,您可以使用TCP / IP跟踪工具(例如Wireshark)查看您与远程应用程序之间在网络中发送的数据包。 它可以帮助您缩小问题是在网络的另一端还是在网络的另一端。
3.使用远程应用程序
有时,由于远程应用程序中的性能问题,事务很可能会变慢。 在这种情况下,您需要使远程应用程序意识到速度变慢,并与他们一起解决问题。
4.非阻塞HTTP客户端
您还可以考虑使用非阻塞HTTP客户端库(例如Grizzly或Netty) ,它们没有阻塞操作来挂起线程。 但是,此解决方案是更具战略意义的解决方案,涉及代码更改和全面的测试。
最后的笔记
请记住,这是一个完整的列表,但可能不是潜在解决方案的完整列表。 如果您想将其他解决方案和超时设置添加到此博客,请在下面的评论中给我留言。 我很高兴用您的推荐来更新此博客。
翻译自: https://jaxenter.com/java-socketinputstream-socketread0-api-156837.html