1 連接被回收
(1)錯誤日誌
Caused by: java.sql.SQLException: connection holder is
null
at com.alibaba.druid.pool.DruidPooledConnection.checkState(DruidPooledConnection.java:
1085
)
at com.alibaba.druid.pool.DruidPooledConnection.getMetaData(DruidPooledConnection.java:
825
)
at org.springframework.jdbc.support.JdbcUtils.extractDatabaseMetaData(JdbcUtils.java:
285
)
...
70
more
ERROR: (DruidDataSource.java:
1815
) abandon connection, open stackTrace
at java.lang.Thread.getStackTrace(Thread.java:
1588
)
at com.alibaba.druid.pool.DruidDataSource.getConnectionDirect(DruidDataSource.java:
942
)
at com.alibaba.druid.filter.FilterChainImpl.dataSource_connect(FilterChainImpl.java:
4534
)
at com.alibaba.druid.filter.stat.StatFilter.dataSource_getConnection(StatFilter.java:
661
)
at com.alibaba.druid.filter.FilterChainImpl.dataSource_connect(FilterChainImpl.java:
4530
)
at com.alibaba.druid.pool.DruidDataSource.getConnection(DruidDataSource.java:
880
)
at com.alibaba.druid.pool.DruidDataSource.getConnection(DruidDataSource.java:
872
)
(2)異常分析
由於connection holder is
null,所以在druid的源碼中找有關連接的holder,在
com.alibaba.druid.pool包下有一個DruidConnectionHolder,源碼如下圖
通過源碼結構可以判定DruidConnectionHolder是連接和事物的關聯,holder中持有一個連接。數據庫事物commit之前的所有操作都要基於同一個Connection,所以可以推斷出holder is null 就是連接被釋放了,
根據詳細的異常堆信息 at com.alibaba.druid.pool.DruidDataSource.getConnectionDirect(DruidDataSource.java:
942
)查看源碼
getConnectionDirect方法中有一段代碼會回收Connection,如下圖
決定是否回收Connection的條件是removeAbandoned, 然後把連接放到activeConnections中,然後在DruidConnectionHolder的init()方法中發現連接銷燬線程
如上圖 銷燬連接的方法判斷執行時間是否大於removeAbandonedTimeoutMillis,如果大於則銷燬,然後發現項目中連接池參數設置配置瞭如下
<property name="removeAbandoned" value="true"/> <property name="removeAbandonedTimeout" value="60"/>
開啓了removeAbandoned且設置了1分鐘,由於項目中執行了一個盤點的worker,時長較長執行邏輯超過設置的閥值,連接被釋放,觸發上面的異常。
(3) 解決方案
removeAbandoned和removeAbandonedTimeout的設計初衷是爲了防止連接泄露的情況發生,所以一定要配置
所以首先調長removeAbandonedTimeout時間,重新上線把中斷的業務跑過,
然後優化對應的執行邏輯,縮減運行時間,最終把時長設定在180秒(視業務而定)