

mybatis系列-connection连接池解析
source link: https://nicksxs.me/2023/02/19/mybatis%E7%B3%BB%E5%88%97-connection%E8%BF%9E%E6%8E%A5%E6%B1%A0%E8%A7%A3%E6%9E%90/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

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();
}
}
Recommend
-
41
之前用 Go 编写过一个简单的服务器和客户端,用来测试 Go 的 HTTP 性能,无意中发现了一个奇怪的问题。 在我的 Mac 上客户端程序会非常稳定地遇到 Connection Reset 的错误,让人一头雾水。 奇怪的 Connect...
-
9
Mybatis居然可以连接 ClickHouse? 环境:springboot 2.4整合技术:durid + mybatis + clickhouse最近在做数据分析项目,里面有这样一个业务:把匹配的数据打上标签,放到新的索引中。数据...
-
6
在 MyBatis 中,使用 PooledDataSource 数据源作为连接池对象,在连接池中存储的是 PooledConnection 对象。通过动态代理,实现对原始连接对象的复用,以及多线程下数据库连接之间的隔离。1. 数据源配置在 mybatis-config.xml 配置文件中,可以...
-
8
WordPress升级报错‘cURL error 28: Connection timed out’连接超时解决方法 2022-04-0609:34:06评论1998字...
-
5
HTTP/2 和 HTTP/3 中禁止了特定于连接的标头字段,如Connection 和 Keep-Alive.
-
1
创建一个SpringBoot项目其他不赘叙了,引入MyBaties、MySql依赖
-
3
mybatis系列-dataSource解析 2023-01-08 Java , Mybatis 3 49
-
5
从零开始Mybatis连接数据库 创建Maven文件 File-->new-->project-->maven,点击next
-
14
解决 SSH 连接 GitHub 出现 Connection Closed 2023.05.15 做个技术宅 0 3...
-
4
mybatis系列-foreach 解析在 org.apache.ibatis.builder.xml.XMLConfigBuilder#parseConfiguration 中进行配置解析,其中这一行就是解析 mappersmapperElement(root.evalNode("mappers"));具体的代码会执...
About Joyk
Aggregate valuable and interesting links.
Joyk means Joy of geeK