簡體   English   中英

Oracle JDBC 間歇性連接問題

[英]Oracle JDBC intermittent Connection Issue

我遇到了一個很奇怪的問題這是JDBC連接到Oracle數據庫的一個非常簡單的使用

OS: Ubuntu
Java Version:  1.5.0_16-b02
               1.6.0_17-b04
Database: Oracle 11g Release 11.1.0.6.0

當我使用 jar 文件OJDBC14.jar它每次都連接到數據庫當我使用 jar 文件OJDBC5.jarOJDBC5.jar會連接,有時會拋出錯誤(如下所示)如果我用 Java 6 重新編譯並使用OJDBC6.jar我得到與OJDBC5.jar相同的結果

我需要 JODB5.jar 中的特定功能,這些功能在 OJDBC14.jar 中不可用

有任何想法嗎

錯誤

> Connecting to oracle
    java.sql.SQLException: Io exception: Connection reset
    at oracle.jdbc.driver.SQLStateMapping.newSQLException(SQLStateMapping.java:74)
    at oracle.jdbc.driver.DatabaseError.newSQLException(DatabaseError.java:110)
    at oracle.jdbc.driver.DatabaseError.throwSqlException(DatabaseError.java:171)
    at oracle.jdbc.driver.DatabaseError.throwSqlException(DatabaseError.java:227)
    at oracle.jdbc.driver.DatabaseError.throwSqlException(DatabaseError.java:494)
    at oracle.jdbc.driver.T4CConnection.logon(T4CConnection.java:411)
    at oracle.jdbc.driver.PhysicalConnection.<init>(PhysicalConnection.java:490)
    at oracle.jdbc.driver.T4CConnection.<init>(T4CConnection.java:202)
    at oracle.jdbc.driver.T4CDriverExtension.getConnection(T4CDriverExtension.java:33)
    at oracle.jdbc.driver.OracleDriver.connect(OracleDriver.java:474)
    at java.sql.DriverManager.getConnection(DriverManager.java:525)
    at java.sql.DriverManager.getConnection(DriverManager.java:171)
    at TestConnect.main(TestConnect.java:13)

代碼

下面是我正在使用的代碼

import java.io.*;
import java.sql.*;
public class TestConnect {
    public static void main(String[] args) {
        try {
            System.out.println("Connecting to oracle"); 
            Connection con=null;
            Class.forName("oracle.jdbc.driver.OracleDriver");
            con=DriverManager.getConnection(
               "jdbc:oracle:thin:@172.16.48.100:1535:sample",
               "JOHN",
               "90009000");
            System.out.println("Connected to oracle"); 
            con.close();
            System.out.println("Goodbye");
        } catch(Exception e) { e.printStackTrace(); }
    }
}

在一些 OTN 論壇 ( https://kr.forums.oracle.com/forums/thread.jspa?messageID=3699989 ) 中提供了針對此問題的解決方案。 但是,沒有解釋問題的根本原因。 以下是我試圖解釋問題的根本原因。

Oracle JDBC 驅動程序以安全的方式與 Oracle 服務器通信。 驅動程序使用java.security.SecureRandom類來收集熵以保護通信。 此類依賴於本機平台支持來收集熵。

是由操作系統或應用程序收集/生成的隨機性,用於密碼學或其他需要隨機數據的用途。 這種隨機性通常是從硬件來源收集的,要么來自硬件噪聲、音頻數據、鼠標移動,要么來自專門提供的隨機性發生器。 內核收集熵並將其存儲為熵池,並通過特殊文件/dev/random/dev/urandom將隨機字符數據提供給操作系統進程或應用程序。

/dev/random 中讀取會消耗所請求的比特/字節數量的熵池,從而提供加密操作中通常需要的高度隨機性。 如果熵池完全耗盡並且沒有足夠的熵可用,則/dev/random上的讀取操作會阻塞,直到收集到額外的熵。 因此,從/dev/random讀取的應用程序可能會阻塞一段時間。

與上述相反,從/dev/urandom讀取不會阻塞。 /dev/urandom讀取也會耗盡熵池,但是當熵不足時,它不會阻塞而是重用部分讀取的隨機數據中的位。 據說這容易受到密碼分析攻擊。 這是理論上的可能性,因此不鼓勵從/dev/urandom讀取以收集加密操作中的隨機性。

默認情況下, java.security.SecureRandom類從/dev/random文件讀取,因此有時會在隨機時間段內阻塞。 現在,如果讀取操作在所需的時間內沒有返回,則 Oracle 服務器將客戶端(在本例中為 jdbc 驅動程序)超時並通過從其末端關閉套接字來中斷通信。 客戶端從阻塞調用返回后嘗試恢復通信時遇到IO異常。 此問題可能會在任何平台上隨機發生,尤其是從硬件噪聲中收集熵的平台。

正如 OTN 論壇中所建議的,此問題的解決方案是覆蓋java.security.SecureRandom類的默認行為,以使用來自/dev/urandom的非阻塞讀取而不是來自/dev/random的阻塞讀取。 這可以通過將以下系統屬性-Djava.security.egd=file:///dev/urandom 添加到 JVM 來完成。 雖然這對於像 JDBC 驅動程序這樣的應用程序來說是一個很好的解決方案,但對於執行核心加密操作(如加密密鑰生成)的應用程序來說,這是不鼓勵的。

其他解決方案可能是使用可用於平台的不同隨機播種器實現,這些實現不依賴於硬件噪聲來收集熵。 有了這個,您可能仍然需要覆蓋java.security.SecureRandom的默認行為。

增加 Oracle 服務器端的套接字超時也是一種解決方案,但在嘗試此操作之前,應從服務器的角度評估副作用。

我面臨着完全相同的問題。 使用 Windows Vista 我無法重現該問題,但在 Ubuntu 上我不斷重現“連接重置”錯誤。

我發現http://forums.oracle.com/forums/thread.jspa?threadID=941911&tstart=0&messageID=3793101

根據該論壇上的一位用戶的說法:

我用 Oracle 開了一張票,這就是他們告訴我的。

java.security.SecureRandom 是sun 提供的標准API。 在此類提供的各種方法中, void nextBytes(byte[]) 就是其中之一。 此方法用於生成隨機字節。 Oracle 11g JDBC 驅動程序使用此 API 在登錄期間生成隨機數。 使用 Linux 的用戶一直遇到 SQLException("Io exception: Connection reset")。

問題是雙重的

  1. 當 SecureRandom.nextBytes(byte[]) 被調用時,JVM 會嘗試列出 /tmp(或由 -Djava.io.tmpdir 設置的備用 tmp 目錄)中的所有文件。 如果文件數量很大,該方法需要很長時間才能響應,從而導致服務器超時

  2. 方法 void nextBytes(byte[]) 在 Linux 上使用 /dev/random,在某些缺少隨機數生成硬件的機器上,操作速度減慢到使整個登錄過程停止的程度。 最終用戶遇到 SQLException("Io exception: Connection reset")

如果底層操作系統是在故障硬件上運行的 Linux,則升級到 11g 的用戶可能會遇到此問題。

原因 尚未確切確定其原因。 這可能是您的硬件出現問題,也可能是由於某種原因軟件無法從 dev/random 讀取

解決方案更改應用程序的設置,以便將下一個參數添加到 java 命令:

-Djava.security.egd=file:/dev/../dev/urandom

我們在 java.security 文件中進行了此更改,它已消除錯誤。

這解決了我的問題。

“連接重置”錯誤消息通常意味着另一方在嘗試創建連接(握手)期間中止了連接。 這有很多可能的原因。 JDBC 驅動程序中的錯誤、數據庫端超時、數據庫重啟、數據庫可用連接不足、網絡質量差、病毒掃描程序/防火牆/代理不良等。

由於它是間歇性發生的,因此可以或多或少地排除 JDBC 驅動程序中的錯誤。 留下了剩余的可能原因。 我建議首先查看數據庫服務器的日志。

很難說,但如果我要檢查 JDBC 驅動程序的實際版本。 確保它是 11.1.0.6。

Oracle 不在文件名中包含數據庫版本。 因此,11.2 的驅動程序與 11.1 的驅動程序名稱完全相同 - ojdbc5.jar。 我將提取驅動程序 jar 文件,並找到 MANIFEST.MF 文件,這將包含一些版本信息。 確保 JDBC 驅動程序的版本與您的數據庫版本匹配。 我懷疑這可能是版本問題,因為 Oracle 11.1.0.6 下載頁面上沒有名為 ojdbc14.jar 的 jar 文件。

如果版本匹配 - 我沒有想法:)

導致我出現此問題的另一件事是HOSTNAME設置錯誤。 我的連接嘗試被掛在:

"main" prio=10 tid=0x00007f7cc8009000 nid=0x2f3a runnable [0x00007f7cce69e000]
   java.lang.Thread.State: RUNNABLE
        at java.net.Inet4AddressImpl.getLocalHostName(Native Method)
        at java.net.InetAddress.getLocalHost(InetAddress.java:1444)
        at sun.security.provider.SeedGenerator$1.run(SeedGenerator.java:176)
        at sun.security.provider.SeedGenerator$1.run(SeedGenerator.java:162)
        at java.security.AccessController.doPrivileged(Native Method)

因此,請確保您在/etc/hosts/有一個主機名條目。

如果您發出這樣的hostname命令:

$ hostname
my.server.com

您需要在/etc/hosts一行:

127.0.0.1 my my.server.com

根據錯誤https://bugs.openjdk.java.net/browse/JDK-6202721

Java 不會考慮-Djava.security.egd=file:/dev/urandom

它應該是-Djava.security.egd=file:/dev/./urandom

只是為了澄清 - 至少從我們發現的情況來看! 這是在 JDK 發行版中為 Linux 設置隨機器的一個問題——我們在 Java6 中發現了它,但不確定 Java7。 隨機生成器的 linux 語法是 file:///dev/urandom,但文件中的條目(可能是從 Windows 留下/復制的)為 file:/dev/urandom。 因此,Java 可能會使用默認值,也就是 /dev/random。 這在無頭機器上不起作用!!!

我發現此鏈接與64位系統,驅動程序jdbc 11g和連接重置相同的問題: http//forums.oracle.com/forums/thread.jspa?messageID = 3793101

此問題的根本原因與用戶身份驗證版本有關。 對於每個數據庫用戶,多個密碼驗證程序保存在數據庫中。 通常,當您升級數據庫時,一個新的密碼驗證程序將添加到列表中,一個更強大的密碼驗證程序。 以下查詢顯示了每個用戶可用的密碼驗證程序版本。 例如:

SQL> SELECT PASSWORD_VERSIONS FROM DBA_USERS WHERE USERNAME='SCOTT';

PASSWORD_VERSIONS
-----------------
11G 12C

升級到較新的驅動程序時,您可以使用較新版本的驗證程序,因為驅動程序和服務器會協商要使用的最強驗證程序。 這個較新版本的驗證器將更安全,並且將涉及生成更大的隨機數或使用更復雜的散列函數,這可以解釋為什么在建立 JDBC 連接時會出現問題。 正如使用/dev/urandom的其他回復所提到的,通常可以解決這些問題。 您還可以決定降級您的密碼驗證程序,並使較新的驅動程序使用與您以前的驅動程序使用的舊密碼驗證程序相同的密碼驗證程序。 例如,如果您想使用 10G 密碼驗證器(僅用於測試目的),首先您需要確保它可供您的用戶使用。 在服務器上的 sqlnet.ora 中設置SQLNET.ALLOWED_LOGON_VERSION_SERVER=8 然后:

SQL> alter user scott identified by "tiger";

User altered.

SQL> SELECT PASSWORD_VERSIONS FROM DBA_USERS WHERE USERNAME='SCOTT';
PASSWORD_VERSIONS
-----------------
10G 11G 12C

然后,您可以通過設置此 JDBC 屬性oracle.jdbc.thinLogonCapability="o3"來強制 JDBC 瘦驅動程序使用 10G 驗證程序。 如果您遇到錯誤"ORA-28040: No matching authentication protocol"那么這意味着您的服務器不允許使用 10G 驗證程序。 如果是這種情況,那么您需要再次檢查您的配置。

請注意,使用 /dev/urandom 的建議解決方案對我來說第一次確實有效,但在那之后並不總是有效。

我公司的 DBA 切換了“SQL* 網絡橫幅”,無論是否采用上述方法,都為我永久修復了它。

我不知道什么是“SQL* 網絡橫幅”,但我希望通過將這些信息放在這里,如果您有(是)一名 DBA,他(您)就會知道該怎么做。

禁用 SQL Net Banners 拯救了我們

-Djava.security.egd=file:/dev/./urandom 應該是對的! 不是 -Djava.security.egd=file:/dev/../dev/urandom 或 -Djava.security.egd=file:///dev/urandom

從 jenkins 執行 liquibase 時,我遇到了同樣的問題。 偶爾會將此錯誤拋出到輸出中,並且根本不執行 liquibase 更改日志。

提供的解決方案:在jenkin的maven項目中,jdk從jdk8-131更新到任何較新的版本(例如java8-162)。

OracleXETNSListener - 如果該服務被禁用,則必須啟動該服務。

run -> services.msc 

並留意那些服務

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM