当前位置:网站首页>十一、HikariCP源码分析之HouseKeeper
十一、HikariCP源码分析之HouseKeeper
2022-07-29 21:41:00 【InfoQ】
HikariPoolRunnableScheduledThreadPoolExecutorhouseKeepingExecutorService/**
* HouseKeeper用于空闲连接过期
*/
private class HouseKeeper implements Runnable {
private volatile long previous = clockSource.plusMillis(clockSource.currentTime(), -HOUSEKEEPING_PERIOD_MS);
@Override
public void run() {
//①
//刷新通过MBean修改的设置
connectionTimeout = config.getConnectionTimeout();
validationTimeout = config.getValidationTimeout();
leakTask.updateLeakDetectionThreshold(config.getLeakDetectionThreshold());
//②
final long idleTimeout = config.getIdleTimeout();
final long now = clockSource.currentTime();
//检测时间回拨, 即网络对时服务对时钟的调整, 允许 128 毫秒的时间差
if (clockSource.plusMillis(now, 128) < clockSource.plusMillis(previous, HOUSEKEEPING_PERIOD_MS)) {
LOGGER.warn("{} - Retrograde clock change detected (housekeeper delta={}), soft-evicting connections from pool.",
clockSource.elapsedDisplayString(previous, now), poolName);
previous = now;
//连接池中所以的连接都标记删除
softEvictConnections();
//重新创建连
fillPool();
return;
} else if (now > clockSource.plusMillis(previous, (3 * HOUSEKEEPING_PERIOD_MS) / 2)) {
// No point evicting for forward clock motion, this merely accelerates connection retirement anyway
//时钟快了, 没必要调整连接池, 反正是加速了连接的过期, 不影响
LOGGER.warn("{} - Thread starvation or clock leap detected (housekeeper delta={}).", clockSource.elapsedDisplayString(previous, now), poolName);
}
//原来的实现代码如下文件的633-650 行: https://github.com/brettwooldridge/HikariCP/blob/bc010fba486b27ae3d034cc9701e0c4217457ddb/src/main/java/com/zaxxer/hikari/pool/HikariPool.java
// logPoolState("Before cleanup ");
// for (PoolBagEntry bagEntry : connectionBag.values(STATE_NOT_IN_USE)) {
// if (connectionBag.reserve(bagEntry)) {
// if (bagEntry.evicted) {
// closeConnection(bagEntry, "(connection evicted)");
// }
// else if (idleTimeout > 0L && clockSource.elapsedMillis(bagEntry.lastAccess, now) > idleTimeout) {
// closeConnection(bagEntry, "(connection passed idleTimeout)");
// }
// else {
// connectionBag.unreserve(bagEntry);
// }
// }
// }
//
// logPoolState("After cleanup ");
//
// fillPool(); // Try to maintain minimum connections
// }
// 代码中, 先将所有超过空闲时间的连接都关闭, 然后将连接池中的连接再填充到minIdle最小空闲连接数
// 后来有个名为 yaojuncn 的人跟brett提了个 issue, 如下: https://github.com/brettwooldridge/HikariCP/issues/379
// issue的内容就是yaojuncn发现, 清理空闲连接的时候, 连接数会小于minIdle, 极端情况下会是 0, 他认为这样有问题, 服务请求多的时候, 会大量的创建连接, 给数据库造成压力
// 但是brett认为, 创建连接非常快, 极端情况的几率极小, 这不是个问题, 提议yaojuncn使用固定大小的连接池
// 讨论来讨论去, brett终于烦了, 接受了yaojuncn的建议, 并且合并了yaojuncn的 merge request.
// yaojuncn提的实现就是目前的请清理方式, 这个: https://github.com/yaojuncn/HikariCP/commit/cbb1e1cc93d050457ffe9939b67eacd6c6bd97a0
//③
//开始清理超过idleTimeout的空闲连接
previous = now;
String afterPrefix = "Pool ";
if (idleTimeout > 0L) {
//查出连接池中所有的空闲连接
final List<PoolEntry> idleList = connectionBag.values(STATE_NOT_IN_USE);
//空闲连接数量 - 用户配置的最小连接数 = 目前可以回收的连接数, 不明白详见Question①
int removable = idleList.size() - config.getMinimumIdle();
//如果有可以回收的连接
if (removable > 0) {
logPoolState("Before cleanup ");
afterPrefix = "After cleanup ";
// 按照最近访问的实际, 从小到大排序, 排序指标是最后访问时间的时间戳, 时间大的是最近使用的, 从小到大遍历比较合理, 能先清理掉长时间没用的, 不用遍历所有的空闲连接
//如果要清理的连接数够了,那么就不用继续遍历了,可以减少循环次数
Collections.sort(idleList, LAST_ACCESS_COMPARABLE);
for (PoolEntry poolEntry : idleList) {
//判断最后访问时间和当前时间的时间差, 是否超过了用户配置的最大空闲时间, 超过了就将连接变为保留状态
if (clockSource.elapsedMillis(poolEntry.lastAccessed, now) > idleTimeout && connectionBag.reserve(poolEntry)) {
//关闭连接
closeConnection(poolEntry, "(connection has passed idleTimeout)");
//可回收连接数减 1, 如果可回收连接数等于 0, 就是清理完了
if (--removable == 0) {
break; // keep min idle cons
}
}
}
}
}
//记录日志
logPoolState(afterPrefix);
//可能有些连接过期了, 重新填充连接池到用户配置的最小连接数
fillPool(); // Try to maintain minimum connections
}
}
①刷新配置
②时间回拨
idleTimeoutnowidleTimeoutnowclockSource.plusMillis(now, 128) < clockSource.plusMillis(previous, HOUSEKEEPING_PERIOD_MS)clockSource.plusMillis(now, 128)clockSourcepreviousHOUSEKEEPING_PERIOD_MS- 系统时间回退 128 毫秒以内
- 系统时间前进了,具体多长时间不管
previous = now;softEvictConnections();fillPool();③清理过期连接
connectionBag.values(STATE_NOT_IN_USE)int removable = idleList.size() - config.getMinimumIdle();removableCollections.sort(idleList, LAST_ACCESS_COMPARABLE);clockSource.elapsedMillis(poolEntry.lastAccessed, now) > idleTimeout && connectionBag.reserve(poolEntry)clockSource.elapsedMillis(poolEntry.lastAccessed, now)connectionBag.reserve(poolEntry)closeConnectionfillPool()边栏推荐
猜你喜欢

官宣!苏州吴江开发区上线电子劳动合同平台

INFTnews | Forbes Web3 exploration

UDP协议详解

940. Different subsequences II

笔记:fgets函数详解

Add a logo to the upper left corner of the picture, add time and address to the lower left corner, and wrap the line when the address reaches the specified length

GBASE 8s 数据库的智能大对象备份

【LeetCode】36、有效的数独

华为畅享50 Pro评测:HarmonyOS加持 更流畅更安全

GBASE 8s 数据库的恢复
随机推荐
PointPillars 工程复现
5V升压充电8.4V芯片
The world is on fire, Google servers have crashed
5 V booster charge 8.4 V chip
[HDLBits brush questions] Verilog Language (4) Procedures and More Verilog Features section
【ORM框架:Sequelize的查询】
【CVPR2022】A Unified Query-based Paradigm for Point Cloud Understanding
结合布林线理解现货白银走势图的方法
Small program WeChat positioning is not accurate
什么是数据安全性?
TCP协议详解
C语言操作符详解(1)
golang文件行号探索
c#开发知识点总结
爽朗的一天
关于云计算的海量数据存储模型[通俗易懂]
GBASE 8s 自定义存储过程和函数使用
品牌广告投放平台的中台化应用与实践
AI全流程开发难题破解之钥
解决报错 WARNING: IPv4 forwarding is disabled. Networking will not work.