【问题标题】:Oracle 11g: JDBC jobs abort with "IO Error: Connection reset"Oracle 11g:JDBC 作业因“IO 错误:连接重置”而​​中止
【发布时间】:2020-06-17 23:42:59
【问题描述】:

我试图弄清楚为什么我们的 Oracle 11g 行为不端。我们在使用 JDBC 访问数据库的所有作业中都收到以下错误:

Error message: java.sql.SQLRecoverableException: IO Error: Connection reset

在任何人不高兴之前,是的,我已经检查了堆栈溢出的其他相关链接(天知道还有多少其他网站)。到目前为止没有任何帮助,我想知道接下来我可以尝试什么。

过去几个月情况恶化了,唯一改变的是访问数据库的作业在 docker 容器中运行。唯一受影响的数据库是 oracle 11g(我们还有其他几个数据库,包括 oracle 12c)。使用sqlplus访问11g的数据库没有问题。

由于这是一个连接的事情,我自然检查了文件tnsnames.ora 和listener.ora。这是它们包含的内容(有问题的数据库是 SID=TMF)。 tnsnames.ora:

GLOB =
  (DESCRIPTION =
    (ADDRESS = (PROTOCOL = TCP)(HOST = linuxoracle11)(PORT = 1521))
    (CONNECT_DATA =
      (SERVER = DEDICATED)
      (SERVICE_NAME = GLOB)
    )
  )

STATIC =
  (DESCRIPTION =
    (ADDRESS = (PROTOCOL = TCP)(HOST = linuxoracle11)(PORT = 1521))
    (CONNECT_DATA =
      (SERVER = DEDICATED)
      (SERVICE_NAME = STATIC)
    )
  )

TMF =
  (DESCRIPTION =
    (SDU=2048)
    (TDU=2048)
    (ADDRESS = (PROTOCOL = TCP)(HOST = linuxoracle11)(PORT = 1521))
    (CONNECT_DATA =
      (SERVER = SHARED)
      (SERVICE_NAME = TMF)
    )
  )

DEMO =
  (DESCRIPTION =
    (ADDRESS = (PROTOCOL = TCP)(HOST = linuxoracle11)(PORT = 1521))
    (CONNECT_DATA =
      (SERVER = DEDICATED)
      (SERVICE_NAME = DEMO)
    )
  )

HONDADB =
  (DESCRIPTION =
    (ADDRESS = (PROTOCOL = TCP)(HOST = linuxoracle11)(PORT = 1521))
    (CONNECT_DATA =
      (SERVER = DEDICATED)
      (SERVICE_NAME = HONDADB)
    )
  )

TEMP =
  (DESCRIPTION =
    (ADDRESS = (PROTOCOL = TCP)(HOST = linuxoracle11)(PORT = 1521))
    (CONNECT_DATA =
      (SERVER = DEDICATED)
      (SERVICE_NAME = TEMP)
    )
  )

EXTPROC_CONNECTION_DATA =
  (DESCRIPTION =
    (ADDRESS_LIST =
      (ADDRESS = (PROTOCOL = IPC)(KEY = EXTPROC1))
    )
    (CONNECT_DATA =
      (SID = PLSExtProc)
      (PRESENTATION = RO)
    )
  )

listener.ora:

SID_LIST_LISTENER =
  (SID_LIST =
    (SID_DESC =
      (GLOBAL_DBNAME = TMF)
      (ORACLE_HOME = /home/oracle/app/oracle/product/11.2.0/dbhome_1)
      (SID_NAME = TMF)
    )

    (SID_DESC =
      (GLOBAL_DBNAME = STATIC)
      (ORACLE_HOME = /home/oracle/app/oracle/product/11.2.0/dbhome_1)
      (SID_NAME = STATIC)
    )

    (SID_DESC =
      (GLOBAL_DBNAME = DEMO)
      (ORACLE_HOME = /home/oracle/app/oracle/product/11.2.0/dbhome_1)
      (SID_NAME = DEMO)
    )
  )


LISTENER =
  (DESCRIPTION_LIST =
    (DESCRIPTION =
      (ADDRESS = (PROTOCOL = TCP)(HOST = linuxoracle11)(PORT = 1521))
      (ADDRESS = (PROTOCOL = IPC)(KEY = EXTPROC1521))
    )
  )

在我的一生中,我看不出哪里出了问题。侦听器跟踪日志有大量这样的条目:

09-JUN-2020 18:25:21 * service_died * TMF * 12537
09-JUN-2020 18:25:22 * service_register * TMF * 0
09-JUN-2020 18:25:27 * service_died * TMF * 12537
09-JUN-2020 18:25:28 * service_register * TMF * 0
Tue Jun 09 18:25:33 2020
09-JUN-2020 18:25:33 * service_died * TMF * 12537
09-JUN-2020 18:25:34 * service_register * TMF * 0
09-JUN-2020 18:25:39 * service_died * TMF * 12537
09-JUN-2020 18:25:40 * service_register * TMF * 0
Tue Jun 09 18:25:45 2020
09-JUN-2020 18:25:45 * service_died * TMF * 12537
09-JUN-2020 18:25:46 * service_register * TMF * 0
09-JUN-2020 18:25:51 * service_died * TMF * 12537
09-JUN-2020 18:25:52 * service_register * TMF * 0
Tue Jun 09 18:25:57 2020
09-JUN-2020 18:25:57 * service_died * TMF * 12537
09-JUN-2020 18:25:58 * service_register * TMF * 0
09-JUN-2020 18:26:03 * service_died * TMF * 12537
09-JUN-2020 18:26:04 * service_register * TMF * 0

我最近尝试解决此问题的方法是对 oracle community post 中的信息稍作改动。服务器上的设置:

[root@linuxoracle11 trace]# mv /dev/random /dev/xrandom
[root@linuxoracle11 trace]# ln -s /dev/urandom /dev/random
[root@linuxoracle11 trace]# ls -l /dev/*ndom
lrwxrwxrwx 1 root root   12 Jun  9 19:26 /dev/random -> /dev/urandom
crw-rw-rw- 1 root root 1, 9 Jun  9 09:58 /dev/urandom
crw-rw-rw- 1 root root 1, 8 Jun  9 09:58 /dev/xrandom

但是,这并没有解决问题,针对数据库运行的作业仍在中止。

最令人恼火的方面是有时作业运行无误。如果有人能暗示我应该朝哪个方向看,我将不胜感激。

编辑: 根据要求,这里是 sqlnet.ora 文件的内容:

SQLNET.RECV_TIMEOUT=5
SQLNET.INBOUND_CONNECT_TIMEOUT=5

是的,这是安装数据库系统时默认生成的。

还有人认为这可能是由于“登录风暴”造成的。这是值得怀疑的,因为我们在数据库上的实际用户很少。主要是我写的关于在 docker 容器中运行的那些工作。我检查了 oracle doco 和一些信息(再次来自 Burleson 咨询页面)并尝试了以下操作:

SQL> select resource_name, current_utilization, limit_value
  2  from v_$resource_limit
  3  where resource_name in ('processes', 'sessions');

RESOURCE_NAME                  CURRENT_UTILIZATION LIMIT_VALU
------------------------------ ------------------- ----------
processes                      25                  200
sessions                       30                  322

所以我们说的是几十个连接,而不是几百个。

Edit2:为了完整起见,这里是堆栈跟踪的一部分:

****** Jun 12, 2020 9:20:32 PM IO Error: Connection reset ******
java.sql.SQLRecoverableException: IO Error: Connection reset
    at oracle.jdbc.driver.T4CConnection.logon(T4CConnection.java:752)
    at oracle.jdbc.driver.PhysicalConnection.connect(PhysicalConnection.java:666)
    at oracle.jdbc.driver.T4CDriverExtension.getConnection(T4CDriverExtension.java:32)
    at oracle.jdbc.driver.OracleDriver.connect(OracleDriver.java:566)
    at java.sql.DriverManager.getConnection(DriverManager.java:664)
    at java.sql.DriverManager.getConnection(DriverManager.java:247)
...

Caused by: java.net.SocketException: Connection reset
    at java.net.SocketOutputStream.socketWrite(SocketOutputStream.java:115)
    at java.net.SocketOutputStream.write(SocketOutputStream.java:155)
    at oracle.net.ns.DataPacket.send(DataPacket.java:209)
    at oracle.net.ns.NetOutputStream.flush(NetOutputStream.java:215)
    at oracle.net.ns.NetInputStream.getNextPacket(NetInputStream.java:302)
    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:429)
    at oracle.jdbc.driver.T4CTTIfun.receive(T4CTTIfun.java:397)
    at oracle.jdbc.driver.T4CTTIfun.doRPC(T4CTTIfun.java:257)
    at oracle.jdbc.driver.T4CTTIoauthenticate.doOAUTH(T4CTTIoauthenticate.java:437)
    at oracle.jdbc.driver.T4CTTIoauthenticate.doOAUTH(T4CTTIoauthenticate.java:954)
    at oracle.jdbc.driver.T4CConnection.logon(T4CConnection.java:639)
    ... 38 more

【问题讨论】:

  • 当它起作用时,它需要多长时间 - 不到 5 秒?你能包括你的sqlnet.ora 文件工具它有sent_timeout 和recv_timeout 设置(大概是5)吗?
  • 我已将sqlnet.ora 的内容添加到帖子中。至于需要多长时间,好吧,我们正在谈论连接到数据库通常需要多长时间。我写的关于在不同时间创建 jdbc 连接的作业,用于读取数据、读取目录数据、更新表等。为什么它有时会起作用,有时会不起作用,这就是我需要弄清楚的。
  • 它是否有效取决于 JDBC 调用是否与 service_died 消息一致,大概(再次!)。比较日志应该可以说明这一点。通话时间越短,越有可能侥幸逃脱;我认为,持续超过 5 秒的操作总是会失败。至于 SQL*Plus,这是一个本地遗赠连接吗?它不依赖于 SQL*Net,因此不会受到影响?如果您通过服务名称连接,您是否看到问题 - 例如sqlplus usr/pwd@//linuxoracle11:1521/TMF ?
  • @Alex Poole:比较日志?哪个?这对我来说是个问题,因为 Oracle 在各种地方都隐藏了日志和警报。我通常需要进行复杂的“查找”才能有机会找到一个。您对 sqlplus 的猜测是正确的。它在服务器上运行,因此不受 sqlnet 问题的影响。
  • 您已经拥有的监听器跟踪日志,带有service_died 消息和时间戳;以及您的应用程序日志显示 JDBC 错误及其时间戳(希望在此之前提供一些信息)。当您收到 JDBC 错误时,它是否与 service_died 一致?当您没有收到 JDBC 错误时,时间是否显示service_registered 和service_died 之间正在发生?我很确定是这样,检查日志只会确认确实如此。

标签: oracle jdbc


【解决方案1】:

这里是 sqlnet.ora 文件的内容:

SQLNET.RECV_TIMEOUT=5
SQLNET.INBOUND_CONNECT_TIMEOUT=5

这似乎是问题所在。日志在service_register 之后五秒重复显示service_died 消息。该服务一遍又一遍地脱离和重新连接。如果您的 JDBC 调用碰巧在注册/死亡对之间开始和结束,它们就会起作用;如果他们开始但在报告service_died 时尚未完成,则失败。

根据the documentation,这应该不是问题 - 毕竟,它们显示这些值设置为 3。

这似乎是一个已知问题;如果您有权访问 My Oracle Support,请查看 Doc ID 2162994.1,它建议将这些设置增加到大于 60 或完全删除它们 - 您可能需要退回数据库以清理它。 (如果您无权访问 MoS,您可以看到 the public outline,它并不能告诉您太多信息,但与您在侦听器日志中看到的类似。)

还有其他问题,在 MoS 中搜索会显示 Doc ID 1206583.1 和其他问题,因此可能存在您可以解决或修补的错误 - 这在一定程度上取决于您使用的 Oracle 版本和补丁级别。如果有疑问,您当然可以提出服务请求;假设您有支持合同。

【讨论】:

  • 将超时设置为 60 左右可能会在大多数情况下解决问题,但在我们最近的计划和审查会议上,我们决定停用 11g 并改为设置 19c。顺便说一句:我们没有支持合同。
  • 在设置了一个替换的 19c 数据库后,我决定再次尝试在 11g 上进行设置。最后,我只是删除了 sqlnet.ora altogehter 中的SQLNET.RECV_TIMEOUT 设置。这显然解决了问题。我只能建议这个职位的其他人也尝试一下。
【解决方案2】:

您应该提供完整的堆栈跟踪 当堆栈跟踪包含:

在 oracle.jdbc.driver.T4CConnection.logon

然后在身份验证期间,数据库重置了连接。 (不是通过途中的任何网络设备)。 这种情况(登录风暴)的根本原因问题实际上是客户端的问题。由于缺少随机数,客户端无法足够快地验证自己。发生这种情况时,您可以:

  • 调整内核参数以在缓冲区(客户端)中保存更多随机数
  • 在 JVM 使用另一个 /dev/*random 设备(客户端)时使用该技巧
  • 更改参数 SQLNET.INBOUND_CONNECT_TIMEOUT 以延长服务器等待客户端(数据库服务器)的时间窗口

但您收到此错误可能还有其他原因。

PS:JVM 具有针对第二个“解决方案”的明确保护(Java 错误 6202721)。因为它会损害安全性。因此,如果您设置“java.security.egd=file:/dev/urandom”,它会故意忽略您。虽然此设备名称未列入黑名单 "java.security.egd=file:/dev/./urandom"

【讨论】:

  • 我已将 docker-compose 配置更改为 "java.security.egd=file:///dev/./urandom"。当我们在没有 docker 的情况下运行时,我们在配置中有这个,但由于未知原因,当我们转移到 docker 时它被更改为"java.security.egd=file:///dev/urandom"。谢谢你的提示。我一定会让你知道它是否有效。
  • 对于 Docker,您确实与其他容器共享相同的 /dev/random 内核设备。为了证明您确实应该至少提供 Java 堆栈跟踪的片段。
  • 我已将堆栈跟踪添加到描述中。它并没有真正增加已经知道的东西。经过一次规划和审查会议,我们现在决定将 oracle 11g 替换为 oracle 19c。这意味着这个问题不再相关。我希望讨论可以帮助其他人深入了解此错误消息的性质。
  • 根据这个stackoverflow.com/a/59097932/836215看来升级到Java11后它得到了修复(因为Java11,JVM默认使用非阻塞/dev/urandom)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2019-12-25
  • 2013-03-06
  • 2014-02-06
  • 2020-01-05
  • 2020-10-04
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多