dubbo 項目用的 commons-dbcp-1.4 和 commons-pool-1.5.4 實現連接池,導致數據庫經常爆滿,經過調試,發現了個 dbcp的bug:
dbcp 源碼:
BasicDataSource.java
創建datasource的方法:
protected synchronized DataSource createDataSource() throws SQLException { if (closed) { throw new SQLException("Data source is closed"); } // Return the pool if we have already created it if (dataSource != null) { return (dataSource); } // create factory which returns raw physical connections ConnectionFactory driverConnectionFactory = createConnectionFactory(); // create a pool for our connections createConnectionPool(); // Set up statement pool, if desired GenericKeyedObjectPoolFactory statementPoolFactory = null; if (isPoolPreparedStatements()) { statementPoolFactory = new GenericKeyedObjectPoolFactory(null, -1, // unlimited maxActive (per key) GenericKeyedObjectPool.WHEN_EXHAUSTED_FAIL, 0, // maxWait 1, // maxIdle (per key) maxOpenPreparedStatements); } // Set up the poolable connection factory createPoolableConnectionFactory(driverConnectionFactory, statementPoolFactory, abandonedConfig); // Create and return the pooling data source to manage the connections createDataSourceInstance(); try { for (int i = 0 ; i < initialSize ; i++) { connectionPool.addObject(); } } catch (Exception e) { throw new SQLNestedException("Error preloading the connection pool", e); } return dataSource; }
該方法先 調用 createConnectionPool() 方法 創建 connectionPool,
接着 調用 createPoolableConnectionFactory() 方法創建 connectionFactory,
最后 再調用 createDataSourceInstance() 方法創建 datasource
看看 createConnectionPool() 方法實現:
protected void createConnectionPool() { // Create an object pool to contain our active connections GenericObjectPool gop; if ((abandonedConfig != null) && (abandonedConfig.getRemoveAbandoned())) { gop = new AbandonedObjectPool(null,abandonedConfig); } else { gop = new GenericObjectPool(); } gop.setMaxActive(maxActive); gop.setMaxIdle(maxIdle); gop.setMinIdle(minIdle); gop.setMaxWait(maxWait); gop.setTestOnBorrow(testOnBorrow); gop.setTestOnReturn(testOnReturn); gop.setTimeBetweenEvictionRunsMillis(timeBetweenEvictionRunsMillis); gop.setNumTestsPerEvictionRun(numTestsPerEvictionRun); gop.setMinEvictableIdleTimeMillis(minEvictableIdleTimeMillis); gop.setTestWhileIdle(testWhileIdle); connectionPool = gop; }
這個方法是:每次調用都會創建一個connectionPool,並且都是直接new 的,
問題:
當創建 connectionPool成功,但是創建connectionFactory失敗,也就是說createConnectionPool()成功了,但是createPoolableConnectionFactory()出現異常了,則dataSource也創建失敗,系統會再次執行 createDataSource() 方法,會再次創建一個新的 coonnectionPool , 我的系統里,一共嘗試創建了 15次都失敗,最后16次才成功,所以也就創建了16個connectionPool,每一個pool的連接數會至少等於 minIdel 的數量,結果導致數據庫連接爆滿。。。
解決:
修改 createConnectionPool() 方法實現,添加非空判斷:
protected void createConnectionPool() { //添加非空判斷 if(gop != null){ return ; } // Create an object pool to contain our active connections GenericObjectPool gop; if ((abandonedConfig != null) && (abandonedConfig.getRemoveAbandoned())) { gop = new AbandonedObjectPool(null,abandonedConfig); } else { gop = new GenericObjectPool(); } gop.setMaxActive(maxActive); gop.setMaxIdle(maxIdle); gop.setMinIdle(minIdle); gop.setMaxWait(maxWait); gop.setTestOnBorrow(testOnBorrow); gop.setTestOnReturn(testOnReturn); gop.setTimeBetweenEvictionRunsMillis(timeBetweenEvictionRunsMillis); gop.setNumTestsPerEvictionRun(numTestsPerEvictionRun); gop.setMinEvictableIdleTimeMillis(minEvictableIdleTimeMillis); gop.setTestWhileIdle(testWhileIdle); connectionPool = gop; }
至此:問題解決。