起因 bonecp不具備回縮功能,即連接池持有連接之後,不會主動去釋放這些連接(即使這些連接始終處於空閑狀態),因此在使用一段時間之後,連接池會達到配置的最大值。 這種方式一定程度上造成了資源的浪費。 改造 參考tomcat jdbc的策略,每隔一段時間(可配置)會啟動定時任務掃描partition ...
起因
bonecp不具備回縮功能,即連接池持有連接之後,不會主動去釋放這些連接(即使這些連接始終處於空閑狀態),因此在使用一段時間之後,連接池會達到配置的最大值。
這種方式一定程度上造成了資源的浪費。
改造
參考tomcat-jdbc的策略,每隔一段時間(可配置)會啟動定時任務掃描partition中的idle隊列,判斷idle連接數是否大於partition可持有的最小連接數,如果是,則啟動清理方法,將連接釋放掉。
為了達到這個目的,實現了ConnectionCleanThread類:
package com.jolbox.bonecp;
import java.sql.SQLException;
import java.util.concurrent.BlockingQueue;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class ConnectionCleanThread implements Runnable {
private static final Logger logger = LoggerFactory.getLogger(ConnectionCleanThread.class);
private ConnectionPartition partition;
private BoneCP pool;
protected ConnectionCleanThread(ConnectionPartition connectionPartition, BoneCP pool) {
this.partition = connectionPartition;
this.pool = pool;
}
@Override
public void run() {
BlockingQueue freeQueue = null;
ConnectionHandle connection = null;
//獲得partition的大小
int partitionSize = this.partition.getAvailableConnections();
for (int i = 0; i < partitionSize; i++) {
//得到free連接的queue
freeQueue = this.partition.getFreeConnections();
//如果空閑連接大於partition的最小允許連接數,回縮到最小允許連接數
while (freeQueue.size() > this.partition.getMinConnections()) {
connection = freeQueue.poll();
connection.lock();
closeConnection(connection);
connection.unlock();
}
}
}
/** Closes off this connection
* @param connection to close
*/
private void closeConnection(ConnectionHandle connection) {
if (connection != null && !connection.isClosed()) {
try {
connection.internalClose();
} catch (SQLException e) {
logger.error("Destroy connection exception", e);
} finally {
this.pool.postDestroyConnection(connection);
connection.getOriginatingPartition().getPoolWatchThreadSignalQueue().offer(new Object()); // item being pushed is not important.
}
}
}
}
同時需要對核心類ConnectionHandle進行改造,加上連接的上鎖方法:
protected void lock() {
lock.writeLock().lock();
}
protected void unlock() {
lock.writeLock().unlock();
}
在BoneCP類的構造器內加上該線程的定時任務:
/**
* 空閑連接清理任務
*/
private ScheduledExecutorService connectionCleanScheduler;
...
this.connectionCleanScheduler = Executors.newScheduledThreadPool(this.config.getPartitionCount(), new CustomThreadFactory("BoneCP-connection-clean-thread"+suffix, true));
...
//定期啟動一個線程清理空閑連接
//add 2017-2-10
final Runnable connectionCleaner = new ConnectionCleanThread(connectionPartition, this);
this.connectionCleanScheduler.scheduleAtFixedRate(connectionCleaner, this.config.getConnectionCleanTimeInSeconds(), this.config.getConnectionCleanTimeInSeconds(), TimeUnit.SECONDS);
效果
經過實際測試,可以在後臺自動的回收idle連接。
現在只是實現了功能,各種情況暫時沒有加入考慮,比如沒有判斷該連接是否應該被釋放。