mybatis系列-connection连接池解析
连接池主要是两个逻辑,首先是获取连接的逻辑,结合代码来讲一讲
private PooledConnection popConnection(String username, String password) throws SQLException {
boolean countedWait = false;
PooledConnection conn = null;
long t = System.currentTimeMillis();
int localBadConnectionCount = 0;
// 循环获取连接
while (conn == null) {
// 加锁
lock.lock();
try {
// 如果闲置的连接列表不为空
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) {
// Can create new connection
// 这里创建连接我们之前讲过,
conn = new PooledConnection(dataSource.getConnection(), this);
if (log.isDebugEnabled()) {
log.debug("Created connection " + conn.getRealHashCode() + ".");
}
} else {
// Cannot create new connection
// 进到这个分支了就表示没法创建新连接了,那么怎么办呢,这里引入了一个 poolMaximumCheckoutTime,这代表了我去控制连接一次被使用的最长时间,如果超过这个时间了,我就要去关闭失效它
PooledConnection oldestActiveConnection = state.activeConnections.get(0);
long longestCheckoutTime = oldestActiveConnection.getCheckoutTime();
if (longestCheckoutTime > poolMaximumCheckoutTime) {
// Can claim overdue connection
// 所有超时连接从池中被借出的次数+1
state.claimedOverdueConnectionCount++;
// 所有超时连接从池中被借出并归还的时间总和 + 当前连接借出时间
state.accumulatedCheckoutTimeOfOverdueConnections += longestCheckoutTime;
// 所有连接从池中被借出并归还的时间总和 + 当前连接借出时间
state.accumulatedCheckoutTime += longestCheckoutTime;
// 从活跃连接数中移除此连接
state.activeConnections.remove(oldestActiveConnection);
// 如果该连接不是自动提交的,则尝试回滚
if (!oldestActiveConnection.getRealConnection().getAutoCommit()) {
try {
oldestActiveConnection.getRealConnection().rollback();
} catch (SQLException e) {
/*
Just log a message for debug and continue to execute the following
statement like nothing happened.
Wrap the bad connection with a new PooledConnection, this will help
to not interrupt current executing thread and give current thread a
chance to join the next competition for another valid/good database
connection. At the end of this loop, bad {@link @conn} will be set as null.
*/
log.debug("Bad connection. Could not roll back");
}
}
// 用此连接的真实连接再创建一个连接,并设置时间
conn = new PooledConnection(oldestActiveConnection.getRealConnection(), this);
conn.setCreatedTimestamp(oldestActiveConnection.getCreatedTimestamp());
conn.setLastUsedTimestamp(oldestActiveConnection.getLastUsedTimestamp());
oldestActiveConnection.invalidate();
if (log.isDebugEnabled()) {
log.debug("Claimed overdue connection " + conn.getRealHashCode() + ".");
}
} else {
// Must wait
// 这样还是获取不到连接就只能等待了
try {
// 标记状态,然后把等待计数+1
if (!countedWait) {
state.hadToWaitCount++;
countedWait = true;
}
if (log.isDebugEnabled()) {
log.debug("Waiting as long as " + poolTimeToWait + " milliseconds for connection.");
}
long wt = System.currentTimeMillis();
// 等待 poolTimeToWait 时间
condition.await(poolTimeToWait, TimeUnit.MILLISECONDS);
// 记录等待时间
state.accumulatedWaitTime += System.currentTimeMillis() - wt;
} catch (InterruptedException e) {
// set interrupt flag
Thread.currentThread().interrupt();
break;
}
}
}
}
// 如果连接不为空
if (conn != null) {
// ping to server and check the connection is valid or not
// 判断是否有效
if (conn.isValid()) {
if (!conn.getRealConnection().getAutoCommit()) {
// 回滚未提交的
conn.getRealConnection().rollback();
}
conn.setConnectionTypeCode(assembleConnectionTypeCode(dataSource.getUrl(), username, password));
// 设置时间
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 + poolMaximumLocalBadConnectionTolerance)) {
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.");
}
}
}
} finally {
// 释放锁
lock.unlock();
}
}
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.");
}
// fanhui
return conn;
}
然后是还回连接
protected void pushConnection(PooledConnection conn) throws SQLException {
// 加锁
lock.lock();
try {
// 从活跃连接中移除当前连接
state.activeConnections.remove(conn);
if (conn.isValid()) {
// 当前的空闲连接数小于连接池中允许的最大空闲连接数
if (state.idleConnections.size() < poolMaximumIdleConnections && conn.getConnectionTypeCode() == expectedConnectionTypeCode) {
// 记录借出时间
state.accumulatedCheckoutTime += conn.getCheckoutTime();
if (!conn.getRealConnection().getAutoCommit()) {
// 同样是做回滚
conn.getRealConnection().rollback();
}
// 新建一个连接
PooledConnection newConn = new PooledConnection(conn.getRealConnection(), this);
// 加入到空闲连接列表中
state.idleConnections.add(newConn);
newConn.setCreatedTimestamp(conn.getCreatedTimestamp());
newConn.setLastUsedTimestamp(conn.getLastUsedTimestamp());
// 原连接失效
conn.invalidate();
if (log.isDebugEnabled()) {
log.debug("Returned connection " + newConn.getRealHashCode() + " to pool.");
}
// 提醒前面等待的
condition.signal();
} else {
// 上面是相同的,就是这里是空闲连接数已经超过上限
state.accumulatedCheckoutTime += conn.getCheckoutTime();
if (!conn.getRealConnection().getAutoCommit()) {
conn.getRealConnection().rollback();
}
conn.getRealConnection().close();
if (log.isDebugEnabled()) {
log.debug("Closed connection " + conn.getRealHashCode() + ".");
}
conn.invalidate();
}
} else {
if (log.isDebugEnabled()) {
log.debug("A bad connection (" + conn.getRealHashCode() + ") attempted to return to the pool, discarding connection.");
}
state.badConnectionCount++;
}
} finally {
lock.unlock();
}
}