今天在一臺配置很低的機器上運行批量更新的程序~~~java
大概跑了三十分鐘~~~這配置~~~這程序~~~spring
而後華麗麗的報異常了~~~sql
具體異常是這樣的,ui
DEBUG: (BaseJdbcLogger.java:132) ooo Using Connection [com.alibaba.druid.proxy.jdbc.ConnectionProxyImpl@4d4e22e1] [2014-07-17 15:19:35]5363945354 [Druid-ConnectionPool-Destory-1422598563] com.alibaba.druid.pool.DruidDataSource:1132 WARN : (DruidDataSource.java:1132) get/close not same thread 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) at com.alibaba.druid.pool.DruidDataSource.getConnection(DruidDataSource.java:97)
這個是最初的異常, 後面還有一大批異常,日誌
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
說什麼holder爲空code
第一眼看到holder就像到Spring的源碼, 裏面處處是holder(笑)rem
可是這裏的holder不是Spirng裏面的,是Druid的get
這個holder大概是用來hou住鏈接池裏面的鏈接的.源碼
而後爲何爲空了呢? 目測是哪一個連接壞了, 或者被意外的關閉了...it
根據異常調源碼 at com.alibaba.druid.pool.DruidDataSource.getConnectionDirect(DruidDataSource.java:942)
if (isRemoveAbandoned()) { StackTraceElement[] stackTrace = Thread.currentThread().getStackTrace(); poolalbeConnection.setConnectStackTrace(stackTrace); poolalbeConnection.setConnectedTimeNano(); poolalbeConnection.setTraceEnable(true); synchronized (activeConnections) { activeConnections.put(poolalbeConnection, PRESENT); } }
看不出啥來. 只能將日誌繼續看看, 仍是看不出啥來
而後看了上面代碼幾遍後, 老以爲 isRemoveAbandoned() 這個方法有鬼.
查看調用處,:
恩, 這個DestroyConnectionThread很是可疑, 跳
if (isRemoveAbandoned()) { removeAbandoned(); }
public int removeAbandoned() { int removeCount = 0; long currrentNanos = System.nanoTime(); List<DruidPooledConnection> abandonedList = new ArrayList<DruidPooledConnection>(); synchronized (activeConnections) { Iterator<DruidPooledConnection> iter = activeConnections.keySet().iterator(); for (; iter.hasNext();) { DruidPooledConnection pooledConnection = iter.next(); if (pooledConnection.isRunning()) { continue; } long timeMillis = (currrentNanos - pooledConnection.getConnectedTimeNano()) / (1000 * 1000); if (timeMillis >= removeAbandonedTimeoutMillis) { iter.remove(); pooledConnection.setTraceEnable(false); abandonedList.add(pooledConnection); } } } ....略 }
擦, 這裏不對頭, timeMillis >= removeAbandonedTimeoutMillis timeMillis 這個是getConnection()被調用時的時間
意思就是一個鏈接被get後, 超過了 removeAbandonedTimeoutMillis這麼久我就弄死你.
而後繼續找removeAbandonedTimeoutMillis 這玩意在哪裏設置的 ,最後發現是在
<property name="removeAbandoned" value="true" />
<property name="removeAbandonedTimeout" value="1800" />
初始化配置的這裏設置的, 這兩個參數的大概意思就是,
經過datasource.getConnontion() 取得的鏈接必須在removeAbandonedTimeout這麼多秒內調用close(),要不我就弄死你.(就是conn不能超過指定的租期)
而後調成2個小時~~~
而後程序成功跑完~~~華麗麗的等了50分鐘
總結:
鏈接池爲了防止程序從池裏取得鏈接後忘記歸還的狀況, 而提供了一些參數來設置一個租期, 使用這個能夠在必定程度上防止鏈接泄漏
可是若是你的業務真要跑這麼久~~~~那仍是注意下這個設置.