mybatis使用了一个PooledDataSource作为数据库连接池.
下面是它的一些变量。
//有一个池状态
private final PoolState state = new PoolState(this);
//里面有一个UnpooledDataSource
private final UnpooledDataSource dataSource;
// OPTIONAL CONFIGURATION FIELDS
//正在使用连接的数量
protected int poolMaximumActiveConnections = 10;
//空闲连接数
protected int poolMaximumIdleConnections = 5;
//在被强制返回之前,池中连接被检查的时间
protected int poolMaximumCheckoutTime = 20000;
//这是给连接池一个打印日志状态机会的低层次设置,还有重新 尝试获得连接,
// 这些情况下往往需要很长时间 为了避免连接池没有配置时静默失 败)。
protected int poolTimeToWait = 20000;
//发送到数据的侦测查询,用来验证连接是否正常工作,并且准备 接受请求。
//默认是“NO PING QUERY SET” ,这会引起许多数据库驱动连接由一 个错误信息而导致失败
protected String poolPingQuery = "NO PING QUERY SET";
//开启或禁用侦测查询
protected boolean poolPingEnabled = false;
//用来配置 poolPingQuery 多次时间被用一次
protected int poolPingConnectionsNotUsedFor = 0;
private int expectedConnectionTypeCode;
PooledDataSource保持了一个PooledState来缓存和处理和connection:
下面是它持有的变量,
protected PooledDataSource dataSource;
//空闲的连接
protected final List<PooledConnection> idleConnections = new ArrayList<PooledConnection>();
//活动的连接
protected final List<PooledConnection> activeConnections = new ArrayList<PooledConnection>();
//----------以下是一些统计信息----------
//请求次数
protected long requestCount = 0;
//总请求时间
protected long accumulatedRequestTime = 0;
protected long accumulatedCheckoutTime = 0;
protected long claimedOverdueConnectionCount = 0;
protected long accumulatedCheckoutTimeOfOverdueConnections = 0;
//总等待时间
protected long accumulatedWaitTime = 0;
//要等待的次数
protected long hadToWaitCount = 0;
//坏的连接次数
protected long badConnectionCount = 0;
从这些变量我们基本上可以看出PooledDataSource的基本实现。可以看出连接池的核心是两个List<PooledConnection>。
PooledDataSource有这么几个方法来处理连接(最下面有带注释的源码)
一.PushConnection()
1.使用syschronized(state)来保证线程安全。
2.创建connection加入空闲连接缓存
二.PopConnection()
1.最外层是个死循环,不断从空闲连接缓存中获取连接,这里使用syschronized(state)来保证线程安全。
2.空闲连接缓存不为空则返回第一个,并在缓存中删除连接。
3.空闲连接缓存为空且活动连接数小于最大活动连接数(默认为10)。则创建并添加空闲连接。
4.活动连接已达上限时,选择checkouttime最长且大于poolMaximumCheckoutTime(默认20000ms)的连接,从缓存删除,并将connection设置为invalidate,
5.以上都不满足则线程wait一段时间(默认为20000ms),并统计一次失败,当失败次数大于最大空闲连接数+3(默认5+3)则抛出异常
三.PingConnection()
用来检测一个connection是否可用,默认不检查,当connection空闲时间大于阈值时(默认为0所以默认不检测)。
默认使用查询语句"NO PING QUERY SET"。
下面是PopConnection带注释的源码
private PooledConnection popConnection(String username, String password) throws SQLException {
boolean countedWait = false;
PooledConnection conn = null;
long t = System.currentTimeMillis();
int localBadConnectionCount = 0;
//最外面是while死循环,如果一直拿不到connection,则不断尝试
while (conn == null) {
synchronized (state) {
if (!state.idleConnections.isEmpty()) {
//如果有空闲的连接的话
// Pool has available connection
//删除空闲列表里第一个,返回
conn = state.idleConnections.remove(0);
if (log.isDebugEnabled()) {
log.debug("Checked out connection " + conn.getRealHashCode() + " from pool.");
}
} else {
//如果没有空闲的连接
// Pool does not have available connection
if (state.activeConnections.size() < poolMaximumActiveConnections) {
//如果activeConnections太少,那就new一个PooledConnection
// Can create new connection
conn = new PooledConnection(dataSource.getConnection(), this);
if (log.isDebugEnabled()) {
log.debug("Created connection " + conn.getRealHashCode() + ".");
}
} else {
//如果activeConnections已经很多了,那不能再new了
// Cannot create new connection
//取得activeConnections列表的第一个(最老的)
PooledConnection oldestActiveConnection = state.activeConnections.get(0);
long longestCheckoutTime = oldestActiveConnection.getCheckoutTime();
if (longestCheckoutTime > poolMaximumCheckoutTime) {
//如果checkout时间过长,则这个connection标记为overdue(过期)
// Can claim overdue connection
state.claimedOverdueConnectionCount++;
state.accumulatedCheckoutTimeOfOverdueConnections += longestCheckoutTime;
state.accumulatedCheckoutTime += longestCheckoutTime;
state.activeConnections.remove(oldestActiveConnection);
if (!oldestActiveConnection.getRealConnection().getAutoCommit()) {
oldestActiveConnection.getRealConnection().rollback();
}
//删掉最老的连接,然后再new一个新连接
conn = new PooledConnection(oldestActiveConnection.getRealConnection(), this);
oldestActiveConnection.invalidate();
if (log.isDebugEnabled()) {
log.debug("Claimed overdue connection " + conn.getRealHashCode() + ".");
}
} else {
//如果checkout时间不够长,等待吧
// Must wait
try {
if (!countedWait) {
//统计信息:等待+1
state.hadToWaitCount++;
countedWait = true;
}
if (log.isDebugEnabled()) {
log.debug("Waiting as long as " + poolTimeToWait + " milliseconds for connection.");
}
long wt = System.currentTimeMillis();
//睡一会儿吧
state.wait(poolTimeToWait);
state.accumulatedWaitTime += System.currentTimeMillis() - wt;
} catch (InterruptedException e) {
break;
}
}
}
}
if (conn != null) {
//如果已经拿到connection,则返回
if (conn.isValid()) {
if (!conn.getRealConnection().getAutoCommit()) {
conn.getRealConnection().rollback();
}
conn.setConnectionTypeCode(assembleConnectionTypeCode(dataSource.getUrl(), username, password));
//记录checkout时间
conn.setCheckoutTimestamp(System.currentTimeMillis());
conn.setLastUsedTimestamp(System.currentTimeMillis());
state.activeConnections.add(conn);
state.requestCount++;
state.accumulatedRequestTime += System.currentTimeMillis() - t;
} else {
if (log.isDebugEnabled()) {
log.debug("A bad connection (" + conn.getRealHashCode() + ") was returned from the pool, getting another connection.");
}
//如果没拿到,统计信息:坏连接+1
state.badConnectionCount++;
localBadConnectionCount++;
conn = null;
if (localBadConnectionCount > (poolMaximumIdleConnections + 3)) {
//如果好几次都拿不到,就放弃了,抛出异常
if (log.isDebugEnabled()) {
log.debug("PooledDataSource: Could not get a good connection to the database.");
}
throw new SQLException("PooledDataSource: Could not get a good connection to the database.");
}
}
}
}
}
if (conn == null) {
if (log.isDebugEnabled()) {
log.debug("PooledDataSource: Unknown severe error condition. The connection pool returned a null connection.");
}
throw new SQLException("PooledDataSource: Unknown severe error condition. The connection pool returned a null connection.");
}
return conn;
}