首先我们看一下几个关键的类
PooledConnection:使用动态代理封装了真正的数据库连接对象;
PoolState:用于管理PooledConnection对象状态的组件,通过两个list分别 管理空闲状态的连接资源和活跃状态的连接资源
PooledDataSource:一个简单,同步的、线程安全的数据库连接池
PooledDataSource的配置和初始化
在示例代码中mybatis-config.xml中如下配置,采用PooledDataSource
mybatis在启动过程中会解析environments节点并创建PooledDataSource
PooledDataSource 获取和归还连接过程
JdbcTransaction.getConnection:
@Override
public Connection getConnection() throws SQLException {
if (connection == null) {
openConnection();
}
return connection;
}
PooledDataSource:
@Override
public Connection getConnection() throws SQLException {
return popConnection(dataSource.getUsername(), dataSource.getPassword()).getProxyConnection();
}
PooledDataSource获取一个的连接的大致流程为:首先判断空闲连接列表是否有值,如果有,则从空闲列表中取出连接进行复用。如果没有,则从活动连接列表中取出第一个连接,判断此连接是否已经超时,如果已经超时,则移除该链接并且复用,如果没有超时,则等待一段时间后继续重新走一遍流程。
流程图如下:
具体代码如下:
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) {
// 加锁
synchronized (state) {
// 如果闲置连接列表不为空
if (!state.idleConnections.isEmpty()) {
// 从闲置连接列表中取出连接
conn = state.idleConnections.remove(0);
if (log.isDebugEnabled()) {
log.debug("Checked out connection " + conn.getRealHashCode() + " from pool.");
}
} else {
// 判断活动连接列表的size是否小于连接池最大的活动连接数量,poolMaximumActiveConnections默认为10
if (state.activeConnections.size() < poolMaximumActiveConnections) {
// 如果小于则创建新的连接
conn = new PooledConnection(dataSource.getConnection(), this);
if (log.isDebugEnabled()) {
log.debug("Created connection " + conn.getRealHashCode() + ".");
}
} else {
// 如果大于则从活动连接列表中获取第一个连接
PooledConnection oldestActiveConnection = state.activeConnections.get(0);
long longestCheckoutTime = oldestActiveConnection.getCheckoutTime();
// 判读此连接是否已经超时
if (longestCheckoutTime > poolMaximumCheckoutTime) {
// 已经超时则更新超时时间
state.claimedOverdueConnectionCount++;
state.accumulatedCheckoutTimeOfOverdueConnections += longestCheckoutTime;
state.accumulatedCheckoutTime += longestCheckoutTime;
// 从活动连接列表中移除此超时连接
state.activeConnections.remove(oldestActiveConnection);
if (!oldestActiveConnection.getRealConnection().getAutoCommit()) {
try {
// 进行回滚操作 确保数据正确
oldestActiveConnection.getRealConnection().rollback();
} catch (SQLException e) {
log.debug("Bad connection. Could not roll back");
}
}
// 用超时连接对象中的真实连接对象Connection创建新的连接PooledConnection
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 {
if (!countedWait) {
//记录等待获取超时连接的个数
state.hadToWaitCount++;
countedWait = true;
}
if (log.isDebugEnabled()) {
log.debug("Waiting as long as " + poolTimeToWait + " milliseconds for connection.");
}
long wt = System.currentTimeMillis();
//等待poolTimeToWait时间后继续走while方法逻辑
state.wait(poolTimeToWait);
state.accumulatedWaitTime += System.currentTimeMillis() - wt;
} catch (InterruptedException e) {
break;
}
}
}
}
if (conn != null) {
// 通过conn.isValid()Ping服务器,检查连接是否有效
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.");
}
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.");
}
}
}
}
}
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;
}
PooledDataSource归还连接的流程:先从活动连接线程列表中移除,然后判断连接是否可用,如果可用的情况下再判断空闲列表个数是否小于最大空闲连接个数,且连接是相同的数据库连接,则将连接加入到空闲连接列表中,并进行唤醒操作。
流程图如下:
具体代码如下:
protected void pushConnection(PooledConnection conn) throws SQLException {
// 加锁
synchronized (state) {
// 从活动连接列表中移除此连接
state.activeConnections.remove(conn);
//ping一下服务器确保连接有效
if (conn.isValid()) {
//判断空闲连接列表的个数是否小于最大空闲连接个数 && 判断连接是否是相同数据库的连接 expectedConnectionTypeCode = ("" + url + username + password).hashCode()
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.");
}
//唤醒正在等待获取超时连接的线程
state.notifyAll();
} 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++;
}
}
}