前言
接上一章继续聊聊链接的问题,之前把mybatis自带的三种数据源做了个大概是说明,还留了个小任务:池化数据源如何获取链接。
很久以前我采用Common-pool工具包实现了一个FTP协议的连接池,现在回过头来看,其实连接池的实现方式都是相似的,本质上来说就是保活、复用的思想,拒绝一次性消耗品。
流程图
说一下主要步骤
- 检查是否有空闲【idle】链接,如果有,则直接返回一个可用的链接;
- 如果没有,则检查activeConnections集合是否达到上限,如果没有满,则创建一个链接,并放到activeConnections集合之后,并返回;
- 如果上两步都不满足,则进行第三步:取出active集合中的首个链接【最老】,判定是否过期,如果过期,则从Active集合移除此链接,用此老链接创建新连接,作废老连接;
- 执行最后一个逻辑分支:等待N秒,再次进入循环;
源码
源码比较长,这里给出关键代码简化版本
private PooledConnection popConnection(String username, String password) throws SQLException {
PooledConnection conn = null;
while (conn == null) {
synchronized (state) {
if (!state.idleConnections.isEmpty()) {
// 从闲置集合中返回
conn = state.idleConnections.remove(0);
} else {
// active集合没有达到上限
if (state.activeConnections.size() < poolMaximumActiveConnections) {
// 直接创建返回
conn = new PooledConnection(dataSource.getConnection(), this);
} else {
// 已经达到上限,取出最老链接
PooledConnection oldestActiveConnection = state.activeConnections.get(0);
long longestCheckoutTime = oldestActiveConnection.getCheckoutTime();
// 检查最老链接自从上次检出后,是否达到过期时间,poolMaximumCheckoutTime默认20秒
if (longestCheckoutTime > poolMaximumCheckoutTime) {
// 用老链接参数创建新连接
conn = new PooledConnection(oldestActiveConnection.getRealConnection(), this);
// 作废老链接
oldestActiveConnection.invalidate();
} else {
// 等待
state.wait(poolTimeToWait);
}
}
}
// 如果获取成功,校验链接是否有效,如果有效,设置参数
if (conn != null) {
// 是否有效
if (conn.isValid()) {
// 设置检出时间
conn.setCheckoutTimestamp(System.currentTimeMillis());
} else {
// 链接无效,重新进入循环
conn = null;
}
}
}
}
return conn;
}
每次拿到链接,都会设置检出时间,如果链接在activeConnections集合中,等一个【poolMaximumCheckoutTime】周期之后,就会作废当前链接(前提是进入那个分支,才会触发)。
需要注意的是:这里的检出时间是可以配置的,默认20秒实在是太久了,万一并发上来了,系统性能瓶颈就会爆发。