前幾天有個項目部署到生成環境中之后,同事反映經常在第一次使用某些業務功能的時候,要等待好久才能加載出數據,然后再次使用該功能,就會很快能加載出數據。
剛聽到這個問題,覺得會不會是數據量太大,第一次查詢很慢,然后由於引入了緩存,所以后面的查詢都很快,但是很快就排除了這個猜測,
因為換了查詢條件,結果依然很快,說明不是緩存的問題,而且直接將sql語句拿到數據庫中去查詢,速度也並不慢。
感性的猜測不成立,開始從理性的角度去排查,首先查看系統日志,果然發現了報錯信息:
WARN [com.atomikos.datasource.pool.ConnectionPool] - atomikos connection pool 'gaOracleDataSource': error creating proxy of connection AtomikosNonXAPooledConnection
com.atomikos.datasource.pool.CreateConnectionException: Error executing testQuery
...
Caused by: java.sql.SQLRecoverableException: IO 錯誤: Software caused connection abort: recv failed
...
Caused by: java.net.SocketException: Software caused connection abort: recv failed
大致意思是:執行testQuery的時候出錯了,原因:軟件導致了連接中斷:接收失敗。
連接中斷?特么的什么問題?數據庫掛了?沒理由啊,掛了,不可能瞬間又好,因為二次訪問的時候,數據加載速度又都正常了,而且也沒有報錯了,之后慢慢總結出,大概20分鍾不訪問某個數據接口,該數據接口就會出現此報錯。
單從報錯信息來看,這里的連接中斷,應該指的的是應用服務器和數據庫服務器之間的連接中斷了,大家都知道,此處的連接采用的是TCP協議,TCP協議中斷,有幾種可能性:
1、連接的過程中,發送方和接收方有一方突然斷開了連接;
2、響應方早在請求發送之前就已經斷開連接,但是發送方並不知曉;
3、請求方早在響應方響應之前就已經斷開連接,但是響應方並不知曉;
對於我們的項目來說,應用程序服務器很明顯是連接的發起方,應用程序報接收失敗,那就是說響應方沒有應答,對應上面的第1和第2中情況,由於響應方是數據庫服務器,經檢查,該服務器的網絡很暢通,也沒有出現過丟包的情況,
所以判斷在連接過程中,突然中斷的可能性很小,那也就是說應該屬於第2種情況,就是數據庫服務器在應用程序發送請求之前,就單方面中斷了連接。
分析到這里,連想到我們的項目是用的atomikos連接池,連接池采用的是tcp長連接,那也就是說其實在數據庫服務器那方對應的接收點已經關閉了連接會話,但是連接池端並不知曉,結果當應用程序從池中獲取到一個連接,嘗試發起溝通的時候,就報錯了。
問題來了,什么情況下數據庫服務器會單方面中斷連接呢?網上查了很多資料,都沒說到重點,后來找到了這篇博文,說的很詳細,和我的推測也基本一致,具體參考:
http://www.360doc.com/content/08/0524/18/49194_1281450.shtml
沒錯,防火牆會定時阻斷那些閑置的TCP連接,估計設置的時間是20-30分鍾左右,解決方法也很簡單,在oracle的sqlnet.ora文件中增加如下代碼:
SQLNET.EXPIRE_TIME=10
意思是:讓oracle每10分鍾,探測一下與之保持連接的客戶端是否還有應答,並關閉與那些無應答的客戶端的連接
OK,各位已經明白原理了吧,oracle每10分鍾發送數據報探測一下客戶端是否有應答,那么防火牆就不會認為這些連接是閑置的連接(因為設置的oracle探測頻率小於防火牆的掃描頻率),也就不會斷開這些連接了。
對於連接池的testQuery機制,是可以在檢測到連接失效后,自動重新創建連接,但是檢測是有間隔的,而且此間隔不能很頻繁,不然也會占用資源,那么如果在檢測間隔內發生了中斷問題,還是會存在數據加載緩慢,給用戶體驗帶來影響的。