解決JDBC Connection Reset的問題分析

JDBC Connection Reset的問題分析

半年前開始,項目組測試MM在驗證功能時,經常報怨講測試環境上的應用在啟動時很慢,偶爾會報失敗,遇到類似問題多數情況下重新啟動一次就可以啟動成功,但少數時候也有反復啟動不成功的案例。

當啟動失敗時,日志裡有如下的異常,看起來似乎和網絡有關。

java.sql.SQLRecoverableException: I/O Exception: Connection reset
at oracle.jdbc.driver.SQLStateMapping.newSQLException(SQLStateMapping.java:281)
at oracle.jdbc.driver.DatabaseError.newSQLException(DatabaseError.java:118)
at oracle.jdbc.driver.DatabaseError.throwSqlException(DatabaseError.java:224)
at oracle.jdbc.driver.DatabaseError.throwSqlException(DatabaseError.java:296)
at oracle.jdbc.driver.DatabaseError.throwSqlException(DatabaseError.java:611)
at oracle.jdbc.driver.T4CConnection.logon(T4CConnection.java:455)
at oracle.jdbc.driver.PhysicalConnection.<init>(PhysicalConnection.java:494)
at oracle.jdbc.driver.T4CConnection.<init>(T4CConnection.java:199)
at oracle.jdbc.driver.T4CDriverExtension.getConnection(T4CDriverExtension.java:30)
at oracle.jdbc.driver.OracleDriver.connect(OracleDriver.java:503)
at java.sql.DriverManager.getConnection(DriverManager.java:582)
at java.sql.DriverManager.getConnection(DriverManager.java:154)

應用使用的數據庫是Oracle,版本為11g R1和R2,Oracle和應用都運行在Linux環境,JDBC驅動是從Oracle官網下載的ojdbc6.jar。

由於這類問題出現的頻率比較低,出現問題的數據庫環境都被做過安全加固,加上我忙於其它事情,這個問題就被擱置起來,沒有去認真定位,測試MM的懷疑都被我以環境原因的理由搪塞過去。

最近兩個月,應用在多個生產環境部署時也出現瞭類似的現象,在有些生產環境,上述問題還會導致雙機切換不成功或者反復切換。做現場實施的同事對此抱怨很多,對我們的應用產生瞭懷疑。

看來這個問題需要認真對待,並且一定要解決瞭。

現象分析

從測試MM和現場實施人員的描述看,這個問題有以下幾個特征:

  • 應用啟動時很慢,這時有很大概率會失敗;
  • 應用包括很多組件,其中大部分組件在啟動時都會嘗試訪問數據庫加載一些數據,而其中一個組件在訪問數據庫時經常會報上述異常;
  • 當啟動失敗時,檢查Oracle實例對應的alert日志時,發現出現有TNS錯誤,樣例如下:

Fatal NI connect error 12170.
  VERSION INFORMATION:
 TNS for Linux: Version 11.2.0.1.0 – Production
 Oracle Bequeath NT Protocol Adapter for Linux: Version 11.2.0.1.0 – Production
 TCP/IP NT Protocol Adapter for Linux: Version 11.2.0.1.0 – Production
  Time: 11-MAY-2014 22:23:40
  Tracing not turned on.
  Tns error struct:
    ns main err code: 12535
    
TNS-12535: TNS:operation timed out
    ns secondary err code: 12560
    nt main err code: 505
    
TNS-00505: Operation timed out
    nt secondary err code: 110
    nt OS err code: 0

問題定位

TNS錯誤

由於實驗室裡的Oracle環境都做過安全加固,而問題現象裡有發現過TNS錯誤,所以剛開始定位問題的思路出瞭點偏差,一直以為和安全加固操作有關,所以尋找的資料也和Oracle相關。

根據網上的資料,在sqlnet.ora文件中定義SQLNET.INBOUND_CONNECT_TIMEOUT變量,經過嘗試,設置為0或者一個比較大的值如30,都可以消除掉前述問題。根據資料介紹,這個變量用來控制客戶端通過認證的時間間隔,假如認證時間超時,則本次數據庫鏈接創建操作就會失敗,而縮短超時時間,可以有效的阻止DoS類型的攻擊。根據安全加固操作指導,加固操作確實包含用於修改認證超時時間的指令。

問題定位到這裡,應該說找到瞭規避手段,也瞭解引發問題的初因,但現場實施人員對於我的解釋並不滿意。好在我又找到一條新線索。

新線索

從Oracle官網論壇裡找到一個帖子,討論的問題和我遇到的問題類似,但提出的問題原因和解決方法比較有意思。按照帖子裡的說法,問題的根因和Java的安全隨機數生成器的實現原理相關。

java.security.SecureRandom is a standard API provided by sun. Among various methods offered by this class void nextBytes(byte[]) is one. This method is used for generating random bytes. Oracle 11g JDBC drivers use this API to generate random number during
login. Users using Linux have been encountering SQLException(“Io exception: Connection
reset”).

The problem is two fold

  • 1.The JVM tries to list all the files in the /tmp (or alternate tmp directory set by -Djava.io.tmpdir) when SecureRandom.nextBytes(byte[]) is invoked. If the number of files is large the method takes a long time to respond and hence cause the server to timeout
  • 2.The method void nextBytes(byte[]) uses /dev/random on Linux and on some machines which lack the random number generating hardware the operation slows down to the extent of bringing the whole login process to a halt. Ultimately the the user encounters SQLException(“Io exception:
  • Connection reset”)

Users upgrading to 11g can encounter this issue if the underlying OS is Linux which is running on a faulty hardware.

Cause

The cause of this has not yet been determined exactly. It could either be a problem in your hardware or the fact that for some reason the software cannot read from /dev/random

Solution

Change the setup for your application, so you add the next parameter to the java command:

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

現場實施人員對於這個帖子裡的信息比較感興趣。按照帖子裡的修改方法,在測試環境和生產環境做瞭多次驗證,驚喜的發現問題得到瞭解決。

隨機數生成器

如果不是為瞭解決問題,平時也不會去刻意查閱底層實現相關的原理,這次是個好機會。網上關於/dev/random的介紹很多,隻列出要點:

  • /dev/random是Linux內核提供的安全隨機數生成設備;
  • /dev/random依賴系統中斷信息來生成隨機數,因而設備數目比較少時,產生隨機數的速度比較慢,當應用對隨機數的需求比較大時會供不應求;
  • /dev/random在讀取時會阻塞調用線程;
  • /dev/urandom是/dev/random的改良版本,解決瞭隨機數生成慢、阻塞調用的問題,但同時稍微降低瞭安全性;

Linux環境下man random命令可以查閱到/dev/random和/dev/urandom的介紹,比較詳盡;

參考資料

https://community.oracle.com/message/3701989

Oracle 11g JDBC driver hangs blocked by /dev/random – entropy pool empty

總結

以上為個人經驗,希望能給大傢一個參考,也希望大傢多多支持WalkonNet。

推薦閱讀: