从Linux到MS SQL Server 2008的JDBC连接在40秒后超时
[见底部更新]
我正在使用JDBC从运行带有2.6.32-32服务器内核的Ubuntu 10.04 LTS的计算机上运行Windows 2008 R2计算机上的SQL Server 2008 R2语句.我正在使用当前的Sun Java 6版本用于Ubuntu(sun-java6-jdk 6.24-1build0.10.04.1)和MS当前的JDBC 3.0驱动程序(sqljdbc_3.0.1301.101_enu). 当一个语句完成时间超过40秒并且它没有返回ResultSet(例如’stmt.executeUpdate(“SELECT * INTO BAR FROM FOO”))时,程序终止并重置连接: Exception in thread "main" com.microsoft.sqlserver.jdbc.SQLServerException: Connection reset at com.microsoft.sqlserver.jdbc.SQLServerConnection.terminate(SQLServerConnection.java:1352) at com.microsoft.sqlserver.jdbc.SQLServerConnection.terminate(SQLServerConnection.java:1339) at com.microsoft.sqlserver.jdbc.TDSChannel.read(IOBuffer.java:1654) at com.microsoft.sqlserver.jdbc.TDSReader.readPacket(IOBuffer.java:3694) at com.microsoft.sqlserver.jdbc.TDSCommand.startResponse(IOBuffer.java:5022) at com.microsoft.sqlserver.jdbc.SQLServerStatement.doExecuteStatement(SQLServerStatement.java:773) at com.microsoft.sqlserver.jdbc.SQLServerStatement$StmtExecCmd.doExecute(SQLServerStatement.java:676) at com.microsoft.sqlserver.jdbc.TDSCommand.execute(IOBuffer.java:4575) at com.microsoft.sqlserver.jdbc.SQLServerConnection.executeCommand(SQLServerConnection.java:1400) at com.microsoft.sqlserver.jdbc.SQLServerStatement.executeCommand(SQLServerStatement.java:179) at com.microsoft.sqlserver.jdbc.SQLServerStatement.executeStatement(SQLServerStatement.java:154) at com.microsoft.sqlserver.jdbc.SQLServerStatement.executeUpdate(SQLServerStatement.java:633) at TestTimeout.main(TestTimeout.java:42) 如果我的语句确实返回ResultSet(例如’ResultSet res = stmt.executeQuery(“SELECT * FROM FOO”)),则连接不会超时. 当我在Win2003R2上运行相同的语句不对SQL2005中的数据库副本返回ResultSet时,该语句在40秒内没有重置连接. 我启用了日志记录,并将完成的SQL2005语句的日志与未完成的SQL2008R2语句进行了比较,并且它们是直线等效的,直到2008查询中的连接重置消息;下午12:54:47看到该行: Jun 6,2011 12:54:07 PM com.microsoft.sqlserver.jdbc.TDSCommand onRequestComplete FINEST: TDSCommand@7ac2b2f6 (SQLServerStatement:1 executeXXX): request complete Jun 6,2011 12:54:07 PM com.microsoft.sqlserver.jdbc.TDSCommand startResponse FINEST: TDSCommand@7ac2b2f6 (SQLServerStatement:1 executeXXX): Reading response... Jun 6,2011 12:54:47 PM com.microsoft.sqlserver.jdbc.TDSChannel read FINE: TDSChannel (ConnectionID:1) read failed:Connection reset Jun 6,2011 12:54:47 PM com.microsoft.sqlserver.jdbc.SQLServerException logException FINE: *** SQLException:ConnectionID:1 com.microsoft.sqlserver.jdbc.SQLServerException: Connection reset Connection reset Jun 6,2011 12:54:47 PM com.microsoft.sqlserver.jdbc.SQLServerException logException FINE: com.microsoft.sqlserver.jdbc.SQLServerConnection.terminate(SQLServerConnection.java:1352)com.microsoft.sqlserver.jdbc.SQLServerConnection.terminate(SQLServerConnection.java:1339)com.microsoft.sqlserver.jdbc.TDSChannel.read(IOBuffer.java:1654)com.microsoft.sqlserver.jdbc.TDSReader.readPacket(IOBuffer.java:3694)com.microsoft.sqlserver.jdbc.TDSCommand.startResponse(IOBuffer.java:5022)com.microsoft.sqlserver.jdbc.SQLServerStatement.doExecuteStatement(SQLServerStatement.java:773)com.microsoft.sqlserver.jdbc.SQLServerStatement$StmtExecCmd.doExecute(SQLServerStatement.java:676)com.microsoft.sqlserver.jdbc.TDSCommand.execute(IOBuffer.java:4575)com.microsoft.sqlserver.jdbc.SQLServerConnection.executeCommand(SQLServerConnection.java:1400)com.microsoft.sqlserver.jdbc.SQLServerStatement.executeCommand(SQLServerStatement.java:179)com.microsoft.sqlserver.jdbc.SQLServerStatement.executeStatement(SQLServerStatement.java:154)com.microsoft.sqlserver.jdbc.SQLServerStatement.executeUpdate(SQLServerStatement.java:633)TestTimeout.main(TestTimeout.java:42) [...] 以下是针对2005数据库的声明中的相应行: Jun 6,2011 2:02:20 PM com.microsoft.sqlserver.jdbc.TDSCommand onRequestComplete FINEST: TDSCommand@4737371 (SQLServerStatement:1 executeXXX): request complete Jun 6,2011 2:02:20 PM com.microsoft.sqlserver.jdbc.TDSCommand startResponse FINEST: TDSCommand@4737371 (SQLServerStatement:1 executeXXX): Reading response... Jun 6,2011 2:02:57 PM com.microsoft.sqlserver.jdbc.TDSChannel logPacket FINEST: /XXX.XXX.XXX.XXX:60091 SPID:73 TDSReader@6 (ConnectionID:1) received Packet:1 (13 bytes) XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX .....I.......... XX XX XX XX XX ..... Jun 6,2011 2:02:57 PM com.microsoft.sqlserver.jdbc.TDSCommand onResponseEOM FINEST: TDSCommand@4737371 (SQLServerStatement:1 executeXXX): disabling interrupts Jun 6,2011 2:02:57 PM com.microsoft.sqlserver.jdbc.TDSReader nextPacket FINEST: TDSReader@6 (ConnectionID:1) Moving to next packet -- unlinking consumed packet Jun 6,2011 2:02:57 PM com.microsoft.sqlserver.jdbc.TDSParser parse FINEST: TDSReader@6 (ConnectionID:1): getNextResult: Processing TDS_DONE (0xFD) [...] 我使用tcpdump捕获SQL Server主机和Linux主机之间的所有流量以及所有ICMP流量,我注意到2008和2005服务器都在语句开始执行后30秒向Linux发送TCP保持活动数据包; Linux主机通过ACK确认来自2005服务器的keep-alive,但是在连接到2008服务器时,Linux主机不发送ACK,2008服务器重新发送保持活动9次(每秒一次),然后重置连接(因此直到超时的40秒时间).现在我注意到Win2003 / SQL2005和Win2008R2 / SQL2008R2主机传输的保持活动包之间存在差异:较新的操作系统使用TCP窗口缩放,窗口大小为66560.所以现在我想知道TCP窗口大小是否> 65535导致Linux机器上的iptables或tcp / ip堆栈无声地忽略该数据包.但是,连接中较早的其他数据包也具有66560的缩放窗口大小,并且它们由Linux服务器确认.日志文件中没有任何内容表明这些数据包正在被丢弃或导致任何类型的问题. 最后一点说明:在追逐这个问题的过程中,由于更新,我们不得不重新启动Linux服务器几次,两次连接运行都没有超时一两天. 所以我很困惑,我希望你们中的一个可能对我有所了解. 更新 我发现我可以通过在Linux服务器上禁用tcp时间戳来消除连接超时.禁用窗口缩放对问题没有影响.追求禁用tcp时间戳的含义对于serverfault.com来说似乎更具问题,所以我将看到在那里迁移这个问题. 更新2 将有效连接(Win2003 / SQL2003)的数据包跟踪(Win2008R2 / SQL2008R2)进行比较,我注意到Win2003连接的keepalive没有选项(即使它在早期数据包中使用tcp时间戳),以及断开连接的keepalive(除非禁用时间戳)确实在keepalive中有tcp选项,即时间戳.所以现在看起来Ubuntu机器在没有tcp选项的情况下响应keepalive但忽略了使用tcp选项的keeplive.这实际上是关于两台主机上的tcp / ip问题的问题. 最后更新 解决方法
事实证明,Win2008发送的带有tcp时间戳的tcp keepalive具有不正确的tcp校验和,这导致Linux主机正确地忽略它们.这个问题几乎肯定是Windows错误,而不是编程或Linux内核问题.见
this thread on the Linux networking dev list.
(编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |