当前位置:网站首页>十一、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()边栏推荐
- 容器网络硬核技术内幕 (22) 安全与自由兼顾
- 【CVPR2022】A Unified Query-based Paradigm for Point Cloud Understanding
- [HDLBits brush questions] Verilog Language (4) Procedures and More Verilog Features section
- "Introduction to nlp + actual combat: Chapter 7: Dataset loading in pytorch and the use of its own datasets"
- mdnice-test
- Leetcode 705.设计哈希集合
- GBASE 8s 如何查看 sbspace 中的可用空间
- 第3章业务功能开发(线索关联市场活动,插入数据并查询)
- 二叉树的操作集:(二叉树的定义,遍历)
- SwiftUI Apple App Store 发布App的初学者完整指南
猜你喜欢

Official announcement!Suzhou Wujiang Development Zone launches electronic labor contract platform

解释器模式

Numpy array processing (2)

linux install redis using script

How to implement your personal knowledge base?

【板栗糖GIS】wps—如何查看表格中的超链接

网安学习-内网渗透2

【R语言】【2】绘图base和lattice和ggplot2库

第3章业务功能开发(线索关联市场活动,插入数据并查询)

结合布林线理解现货白银走势图的方法
随机推荐
leetcode122. Best Time to Buy and Sell Stock II 买卖股票的最佳时机 II(简单)
【板栗糖GIS】wps—如何查看表格中的超链接
B. Party(图论/暴力/顶点的度数)
转:idea中language level设置
相亲信息
GBASE 8s PAM插入式身份验证模块
网络通信编程基础,BIO,NIO
D. Rain(思维/线性代数/差分数组)
SwiftUI CoreData 教程之如何加速搜索速度
linux install redis using script
南信大提出TIPCB,一个简单但有效的用于基于文本的人员搜索的基于部分的卷积baseline
容器网络硬核技术内幕 (小结-下)
Get the Qiniu cloud address file and save it locally
C语言操作符详解(1)
在Ferora35中安装oracle-database-xe-21c
mdnice-test
JS教程之 ElectronJS 自定义标题栏
Xshell 7 提示 “要继续使用此程序,您必须应用最新的更新或使用新版本”
动态规划专题
VSCode 插件大全