Java出问题了吗?

首先

如果在浏览器访问时没有响应,并且模糊地询问服务器端的Java是否挂起,你可能会想知道怀疑Java虚拟机的原因是什么。但可以推测,这是出于以下原因希望按以下顺序进行调查。

    • Java VM もしくは、その上で動作しているミドルウエアとしてサポート担当からの見解が必要

 

    • 問題がなければ、その旨の答えでもOK

 

    サポート担当の見解をもって、次は必要に応じてネットワークもしくはデータベース担当へ問い合わせを行いたい

在这种情况下,我将提供一个常见的例子。

Java线程转储

当我们能够获取到Java线程转储时,可以说明Java虚拟机的功能正常运行,因此可以说Java虚拟机本身并没有挂起,但我们首先需要查看内容。

问题部分的线程转储

"default task-4" #116 prio=5 os_prio=0 tid=0x0000000003ba3800 nid=0x7894 runnable [0x00007f58c80c7000]
   java.lang.Thread.State: RUNNABLE
        at java.net.SocketInputStream.socketRead0(Native Method)
        at java.net.SocketInputStream.socketRead(SocketInputStream.java:116)
        at java.net.SocketInputStream.read(SocketInputStream.java:171)
        at java.net.SocketInputStream.read(SocketInputStream.java:141)
        at oracle.net.ns.Packet.receive(Packet.java:311)
        at oracle.net.ns.DataPacket.receive(DataPacket.java:105)
        at oracle.net.ns.NetInputStream.getNextPacket(NetInputStream.java:305)
        at oracle.net.ns.NetInputStream.read(NetInputStream.java:249)
        at oracle.net.ns.NetInputStream.read(NetInputStream.java:171)
        at oracle.net.ns.NetInputStream.read(NetInputStream.java:89)
        at oracle.jdbc.driver.T4CSocketInputStreamWrapper.readNextPacket(T4CSocketInputStreamWrapper.java:123)
        at oracle.jdbc.driver.T4CSocketInputStreamWrapper.read(T4CSocketInputStreamWrapper.java:79)
        at oracle.jdbc.driver.T4CMAREngineStream.unmarshalUB1(T4CMAREngineStream.java:426)
        at oracle.jdbc.driver.T4CTTIfun.receive(T4CTTIfun.java:390)
        at oracle.jdbc.driver.T4CTTIfun.doRPC(T4CTTIfun.java:249)
        at oracle.jdbc.driver.T4C8Oall.doOALL(T4C8Oall.java:566)
        at oracle.jdbc.driver.T4CStatement.doOall8(T4CStatement.java:202)
        at oracle.jdbc.driver.T4CStatement.doOall8(T4CStatement.java:45)
        at oracle.jdbc.driver.T4CStatement.executeForDescribe(T4CStatement.java:766)
        at oracle.jdbc.driver.OracleStatement.executeMaybeDescribe(OracleStatement.java:897)
        at oracle.jdbc.driver.OracleStatement.doExecuteWithTimeout(OracleStatement.java:1026)
        at oracle.jdbc.driver.OracleStatement.executeQuery(OracleStatement.java:1244)
        - locked <0x00000000fcd35220> (a oracle.jdbc.driver.T4CConnection)
        at oracle.jdbc.driver.OracleStatementWrapper.executeQuery(OracleStatementWrapper.java:420)
        at org.jboss.jca.adapters.jdbc.WrappedStatement.executeQuery(WrappedStatement.java:397)
        at abc.doGet(abc.java:37)
... 後略 ...

如果访问了名为abc的servlet,无法获得响应,那么查看与abc相关的堆栈会显示如上所述的内容。从下往上看,大致是通过jdbc的doExecuteWithTimeout,再到NetInputStream.read,然后是socketRead0,进入了读等待状态。等待了几十秒后,再次获取线程转储,情况没有发生变化。凭直觉来看,由于存在doExecuteWithTimeout,似乎达到了jdbc的超时情况。也就是说,在以下第五个操作中,仍然处于等待状态,无法继续下一步处理。

    1. 通过浏览器向服务器发送HTTP GET请求。

 

    1. 为了生成响应内容,Servlet使用jdbc将SQL提交给oracle数据库。

 

    1. SQL查询在查询超时时间内未能获取结果。

 

    1. 作为超时处理的一部分,使用statement.cancel命令向jdbc发出取消请求。

 

    1. 正在等待对取消请求的响应。

 

    如果可以获得取消请求的响应,将其作为SQLTimeoutException处理,然后执行相应的异常处理…可能是这样。

注意:通过向可以正确响应的 DB 发出请求,statement.cancel 将被取消。

这意味着数据库可能存在问题吗?

从数据库的角度来看

從線程轉儲的內容來看,詢問在那個時間是否在數據庫端發生異常…
完全沒有發現任何異常。本來就要質疑什麼才應該懷疑數據庫?被問到了。

取消的数据包是否已发送?

如果数据库没有问题,我就真的很想知道 statement.cancel 是否被执行并作为数据包发送到数据库。所以,我会重新创建问题,捕获数据包进行确认。

在Java正在运行的系统中

statement.cancel に対応したパケット

13:14:08.948939 IP rhel74.38860 > 192.168.1.25.1521: Flags [P.U], seq 4510:4511, ack 5404, win 44020, urg 1, length 1
..s....
        0x0010:  c0a8 0119 97cc 05f1 f070 d6a2 c3a2 111d  ......-..p......
        0x0020:  5038 abf4 cdeb 0001 21                   P8......!

我們關注Flags[P.U],即PUSH和URG標誌已設置。
並且用戶數據只有一個字符”!”。
從這個信息可以看出,”statement.cancel”被執行,並正確發送了取消請求。

在接收方面呢?
在数据库系统的系统中捕获数据包并进行确认后,没有找到与上述对应的数据包。
由于通过了NAT和多个网络设备,所以看起来数据包在某个地方被丢弃了。
※ 根据我的经验,有时只有TCP的URG标志被丢弃的情况。未设置URG的 “!” 数据包,Oracle数据库端不会有响应。

当数据库无法按预期将语句取消时,对于执行时间较长的SQL,无法超时取消,结果就是无限等待响应。

问题出在网络上。

在网络路径中的某种机制,比如NAT或防火墙,可能无法正确处理带有URG标志的数据包。
因此,根据获取的数据包捕获情况,我认为会向网络负责人询问为什么包含URG标志的数据包无法到达。
可能会被问到你用什么怀疑网络,但是…

一旦问题浮出水面,错误的种类就会多种多样。

在这种等待来自JDBC的响应的情况下,取消操作可能无效,并且这不一定只会产生一种情况。例如,如果在开始事务后,可以通过JTA(Java事务API)进行配置以执行未完成事务的恢复。如果设置为在每30分钟进行一次恢复尝试,直到经过24小时为止,那么即使尝试执行恢复操作,仍然会等待相同的取消响应。而且,每30分钟过去时,恢复线程会增加,并最终可能导致创建了过多的线程,从而导致资源方面的错误,如Too Many Open Files或无法创建新的本机线程等错误。此外,由于不会放弃,所以即使重新启动,也会重复相同的操作。

考虑到这些因素,我认为重要的是检查时间流逝和线程转储的内容,以了解正在发生的事情。

如果以上事项对您有所帮助,那将是我的荣幸。

bannerAds