Oracle JDBC 連接卡死后 Connection Reset解決過程


jstorm項目新增一台linux主機,程序運行的時候報錯如下:


我把研發的源代碼拿過來找到對應的LoadSysParam.init();單獨寫了一個測試程序,在新主機上連續運行幾次后就會重現該問題報錯如下:

Exception in thread "main" org.springframework.jdbc.CannotGetJdbcConnectionException: Could not get JDBC Connection; nested exception is org.apache.commons.dbcp.SQLNestedException: Cannot create PoolableConnectionFactory (IO 錯誤: Connection reset)
    at org.springframework.jdbc.datasource.DataSourceUtils.getConnection(DataSourceUtils.java:80)
    at org.springframework.jdbc.core.JdbcTemplate.execute(JdbcTemplate.java:573)
    at org.springframework.jdbc.core.JdbcTemplate.query(JdbcTemplate.java:637)
    at org.springframework.jdbc.core.JdbcTemplate.query(JdbcTemplate.java:666)
    at org.springframework.jdbc.core.JdbcTemplate.query(JdbcTemplate.java:674)
    at org.springframework.jdbc.core.JdbcTemplate.query(JdbcTemplate.java:714)
    at com.tydic.common.db.DBManager.getSql(DBManager.java:507)
    at com.tydic.common.db.DBManager.getListBySqlCode(DBManager.java:87)
    at com.tydic.notice.work.load.LoadSysParam.LoadMsgStruct(LoadSysParam.java:239)
    at com.tydic.notice.work.load.LoadSysParam.load(LoadSysParam.java:151)
    at com.tydic.notice.work.load.LoadSysParam.init(LoadSysParam.java:142)
    at com.tydic.main.LoadMain.main(LoadMain.java:12)
Caused by: org.apache.commons.dbcp.SQLNestedException: Cannot create PoolableConnectionFactory (IO 錯誤: Connection reset)
    at org.apache.commons.dbcp.BasicDataSource.createPoolableConnectionFactory(BasicDataSource.java:1549)
    at org.apache.commons.dbcp.BasicDataSource.createDataSource(BasicDataSource.java:1388)
    at org.apache.commons.dbcp.BasicDataSource.getConnection(BasicDataSource.java:1044)
    at org.springframework.jdbc.datasource.DataSourceUtils.doGetConnection(DataSourceUtils.java:111)
    at org.springframework.jdbc.datasource.DataSourceUtils.getConnection(DataSourceUtils.java:77)
    ... 11 more
Caused by: java.sql.SQLRecoverableException: IO 錯誤: Connection reset
    at oracle.jdbc.driver.T4CConnection.logon(T4CConnection.java:752)
    at oracle.jdbc.driver.PhysicalConnection.connect(PhysicalConnection.java:662)
    at oracle.jdbc.driver.T4CDriverExtension.getConnection(T4CDriverExtension.java:32)
    at oracle.jdbc.driver.OracleDriver.connect(OracleDriver.java:560)
    at org.apache.commons.dbcp.DriverConnectionFactory.createConnection(DriverConnectionFactory.java:38)
    at org.apache.commons.dbcp.PoolableConnectionFactory.makeObject(PoolableConnectionFactory.java:582)
    at org.apache.commons.dbcp.BasicDataSource.validateConnectionFactory(BasicDataSource.java:1556)
    at org.apache.commons.dbcp.BasicDataSource.createPoolableConnectionFactory(BasicDataSource.java:1545)
    ... 15 more
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:433)
    at oracle.jdbc.driver.T4CTTIoauthenticate.doOAUTH(T4CTTIoauthenticate.java:950)
    at oracle.jdbc.driver.T4CConnection.logon(T4CConnection.java:639)
    ... 22 more

網上搜索"linux oracle connection reset",得到如下信息:

-----------------------------------------------------------------------------------

這絕對是我碰計算機以來遇到的第一大坑!

症狀:

在Linux主機上遠程登錄,執行一個簡單的Oracle的JDBC連接程序(jar包),結果硬生生的卡在了連接建立驗證階段,然后等上幾分鍾后因為連接超時,連接被遠端的Oracle服務器reset,於是報了connection reset exception

原因:

參考:http://www.usn-it.de/index.php/2009/02/20/oracle-11g-jdbc-driver-hangs-blocked-by-devrandom-entropy-pool-empty/

如參考材料中所述,oracle JDBC在建立連接時需要一些隨機數據用以加密session token之類的東西,而這個隨機數據源默認用的是/dev/random,如果不是,反正也是一個能讓人慢的抓狂的發生源。Linux有個內核熵池(感覺太裝B了),通過搜集鍵盤,鼠標,中斷,磁盤操作來產生隨機數據,可以通過以下命令查看當前的熵值:

cat /proc/sys/kernel/random/entropy_avail

由於執行程序的主機沒有圖形界面只是通過遠程ssh進行連接,那么熵值來源就少了兩個,如果機器比較空閑則后面兩個來源也少了,結果就是等半天來不了一個隨機數,可以通過一下命令體驗一下,用/dev/random憋出個隨機數是多難

dd if =/dev/random of=rnd_file bs=1 count=64

如果僥幸執行的很快,可以多試幾次把積累起來的熵值用掉,可以通過前面所述的方法查看當前熵值數目

Linux中還有個隨機數發生器,/dev/urandom,如其名字所述,不那么隨機的隨機發生器,就是偽隨機的,當然會快很多。參考資料中給出的把隨機源修改為/dev/urandom的方法,即在執行java程序加入命令行參數:

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

可是似乎不起作用。那么手工來增大熵值吧,可以執行一下命令

for   i in {1..100000}; do   cat /proc/sys/kernel/random/entropy_avail;done;

就是反復打印當前熵值10萬次,當然根據自己測試的結果當熵值到達240+時,可以按ctrl+c終止這個命令,此時再去執行Oracle JDBC程序,就可以連接成功了(保險一點可以等熵值更大時終止命令)。雖然不是實用的解決方案,但至少確定了問題所在。為了這個事,人都快奔潰了。

 

解決方案:

安裝一個為提供提供熵的程序包

   sudo apt-get install haveged
安裝后需要手動執行
   /usr/sbin/haveged -w 1024 -v 1
--------------------
通過比對新老機器熵值cat /proc/sys/kernel/random/entropy_avail,發現新機器值很小,老機器之很大,感覺這個博客寫的應該靠譜
同時又在啟動參數上加入-Djava.security.egd=file:///dev/urandom,發現程序不會報錯。可以確定就是這個問題導致的。
ps:suse默認是有這個安裝包的,rethad默認無此安裝包,需要另外裝


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM