关于Hikari连接池的源码理解与配置使用

article/2025/9/26 4:51:43

关于Hikari连接池的源码理解与配置使用

1. 连接池初始化

1.1 初始化连接池

对于应用者来说,我们构造一个线程池就是构造一个HikariDataSource。

重点看一下获取连接以及相关连接管理的流程。

public Connection getConnection() throws SQLException {if (this.isClosed()) {throw new SQLException("HikariDataSource " + this + " has been closed.");} else if (this.fastPathPool != null) {return this.fastPathPool.getConnection();} else {HikariPool result = this.pool;if (result == null) {synchronized(this) {result = this.pool;if (result == null) {this.validate();LOGGER.info("{} - Starting...", this.getPoolName());try {this.pool = result = new HikariPool(this);this.seal();} catch (PoolInitializationException var5) {if (var5.getCause() instanceof SQLException) {throw (SQLException)var5.getCause();}throw var5;}LOGGER.info("{} - Start completed.", this.getPoolName());}}}return result.getConnection();}}

可以发现,连接池的构建是在首次获取连接的时候,继续往下查看代码,this.pool = result = new HikariPool(this);

在这里插入图片描述

HikariPool的构造函数中,有两个任务类需要注意的,一是PoolEntryCreator,二是HouseKeeper,前者是初始化连接类,后者是连接池的连接管理类。

	private final class PoolEntryCreator implements Callable<Boolean> {private final String loggingPrefix;PoolEntryCreator(String loggingPrefix) {this.loggingPrefix = loggingPrefix;}public Boolean call() throws Exception {//循环创建连接池核心连接for(long sleepBackoff = 250L; HikariPool.this.poolState == 0 && this.shouldCreateAnotherConnection(); sleepBackoff = Math.min(TimeUnit.SECONDS.toMillis(10L), Math.min(HikariPool.this.connectionTimeout, (long)((double)sleepBackoff * 1.5D)))) {//创建连接PoolEntry poolEntry = HikariPool.this.createPoolEntry();if (poolEntry != null) {//添加连接到concurrentBagHikariPool.this.connectionBag.add(poolEntry);HikariPool.this.LOGGER.debug("{} - Added connection {}", HikariPool.this.poolName, poolEntry.connection);if (this.loggingPrefix != null) {HikariPool.this.logPoolState(this.loggingPrefix);}return Boolean.TRUE;}UtilityElf.quietlySleep(sleepBackoff);}return Boolean.FALSE;}private boolean shouldCreateAnotherConnection() {//判断当前连接数是否小于最大连接数,并且存在等待获取连接的线程或当前连接小于最大连接大于最小空闲连接数return HikariPool.this.getTotalConnections() < HikariPool.this.config.getMaximumPoolSize() && (HikariPool.this.connectionBag.getWaitingThreadCount() > 0 || HikariPool.this.getIdleConnections() < HikariPool.this.config.getMinimumIdle());}}

concurrentBag是存储poolEntity的容器。HouseKeeper会在后面3.1中进行说明。具体看一下createPoolEntry()的代码:

其中涉及到一个maxLifetime参数,即连接的生命周期。默认是30min,可以看到创建对象后,对获取配置的参数maxlifetime,进行一个随机数的处理,原因是避免所有连接的lifetime都相同,导致连接的同时失效,对数据库造成压力。官方推荐可以将maxlifetime的时间设置成比数据库连接断开时间短一点点,也是为了避免长时间未执行,导致连接的全部失效。

同时,每个连接都构建了一个监控任务,当超过了maxLifetime,将连接的状态标记为evict,真正的关闭在获取连接的时候执行,2.1中会说明。

	private PoolEntry createPoolEntry() {try {PoolEntry poolEntry = this.newPoolEntry();long maxLifetime = this.config.getMaxLifetime();if (maxLifetime > 0L) {long variance = maxLifetime > 10000L ? ThreadLocalRandom.current().nextLong(maxLifetime / 40L) : 0L;long lifetime = maxLifetime - variance;poolEntry.setFutureEol(this.houseKeepingExecutorService.schedule(() -> {if (this.softEvictConnection(poolEntry, "(connection has passed maxLifetime)", false)) {this.addBagItem(this.connectionBag.getWaitingThreadCount());}}, lifetime, TimeUnit.MILLISECONDS));}return poolEntry;} catch (Exception var8) {if (this.poolState == 0) {this.LOGGER.debug("{} - Cannot acquire connection from data source", this.poolName, var8 instanceof ConnectionSetupException ? var8.getCause() : var8);}return null;}}

2. 获取连接

2.1 HikariPool.getConnection()

    public Connection getConnection() throws SQLException {return this.getConnection(this.connectionTimeout);}public Connection getConnection(long hardTimeout) throws SQLException {this.suspendResumeLock.acquire();long startTime = ClockSource.currentTime();try {long timeout = hardTimeout;//connectionTimeout内会进行重试,直到获取连接while(true) {//从concurrentBag中获取连接PoolEntry poolEntry = (PoolEntry)this.connectionBag.borrow(timeout, TimeUnit.MILLISECONDS);if (poolEntry != null) {long now = ClockSource.currentTime();//判断连接是否打上evict标记(删除)并且当前连接状态是否正常或者是否超出存活时间if (!poolEntry.isMarkedEvicted() && (ClockSource.elapsedMillis(poolEntry.lastAccessed, now) <= this.ALIVE_BYPASS_WINDOW_MS || this.isConnectionAlive(poolEntry.connection))) {this.metricsTracker.recordBorrowStats(poolEntry, startTime);//获取连接,创建连接代理对象Connection var10 = poolEntry.createProxyConnection(this.leakTaskFactory.schedule(poolEntry), now);return var10;}//对于失效连接,关闭this.closeConnection(poolEntry, poolEntry.isMarkedEvicted() ? "(connection was evicted)" : "(connection is dead)");timeout = hardTimeout - ClockSource.elapsedMillis(startTime);if (timeout > 0L) {continue;}}this.metricsTracker.recordBorrowTimeoutStats(startTime);throw this.createTimeoutException(startTime);}} catch (InterruptedException var14) {Thread.currentThread().interrupt();throw new SQLException(this.poolName + " - Interrupted during connection acquisition", var14);} finally {this.suspendResumeLock.release();}}

3. 管理连接

3.1 HouseKeeper

houseKeeper线程任务每30s执行一次,用来管理空闲连接,其中涉及的参数主要是idelTimeout,看管理空闲连接的代码,List notInUse = HikariPool.this.connectionBag.values(0);获取状态为0( NOT_IN_USER )的连接,关闭大于minimumIdel小于max的空闲连接。

同时,如果池内连接数小于min,则进行填充。

	private final class HouseKeeper implements Runnable {private volatile long previous;private HouseKeeper() {this.previous = ClockSource.plusMillis(ClockSource.currentTime(), -HikariPool.this.HOUSEKEEPING_PERIOD_MS);}public void run() {try {HikariPool.this.connectionTimeout = HikariPool.this.config.getConnectionTimeout();HikariPool.this.validationTimeout = HikariPool.this.config.getValidationTimeout();HikariPool.this.leakTaskFactory.updateLeakDetectionThreshold(HikariPool.this.config.getLeakDetectionThreshold());long idleTimeout = HikariPool.this.config.getIdleTimeout();long now = ClockSource.currentTime();//管理时钟倒退,将连接设置为evictif (ClockSource.plusMillis(now, 128L) < ClockSource.plusMillis(this.previous, HikariPool.this.HOUSEKEEPING_PERIOD_MS)) {HikariPool.this.LOGGER.warn("{} - Retrograde clock change detected (housekeeper delta={}), soft-evicting connections from pool.", HikariPool.this.poolName, ClockSource.elapsedDisplayString(this.previous, now));this.previous = now;HikariPool.this.softEvictConnections();return;}if (now > ClockSource.plusMillis(this.previous, 3L * HikariPool.this.HOUSEKEEPING_PERIOD_MS / 2L)) {HikariPool.this.LOGGER.warn("{} - Thread starvation or clock leap detected (housekeeper delta={}).", HikariPool.this.poolName, ClockSource.elapsedDisplayString(this.previous, now));}this.previous = now;String afterPrefix = "Pool ";//管理空闲连接if (idleTimeout > 0L && HikariPool.this.config.getMinimumIdle() < HikariPool.this.config.getMaximumPoolSize()) {HikariPool.this.logPoolState("Before cleanup ");afterPrefix = "After cleanup  ";List<PoolEntry> notInUse = HikariPool.this.connectionBag.values(0);int toRemove = notInUse.size() - HikariPool.this.config.getMinimumIdle();Iterator var8 = notInUse.iterator();while(var8.hasNext()) {PoolEntry entry = (PoolEntry)var8.next();if (toRemove > 0 && ClockSource.elapsedMillis(entry.lastAccessed, now) > idleTimeout && HikariPool.this.connectionBag.reserve(entry)) {HikariPool.this.closeConnection(entry, "(connection has passed idleTimeout)");--toRemove;}}}HikariPool.this.logPoolState(afterPrefix);HikariPool.this.fillPool();} catch (Exception var10) {HikariPool.this.LOGGER.error("Unexpected exception in housekeeping task", var10);}}}

4. 释放连接

4.1 代理释放连接

housekeeper会定时关闭空闲连接,那么怎么设置连接的状态为0呢?连接使用完后怎么释放?

创建连接的时候可以看到提供的连接是HikariProxyConnection代理。所以用户调用close方法,其实是执行的HikariProxyConnection.close()

ProxyConnection:public final void close() throws SQLException {this.closeStatements();if (this.delegate != ProxyConnection.ClosedConnection.CLOSED_CONNECTION) {this.leakTask.cancel();try {......} catch (SQLException var5) {if (!this.poolEntry.isMarkedEvicted()) {throw this.checkException(var5);}} finally {this.delegate = ProxyConnection.ClosedConnection.CLOSED_CONNECTION;this.poolEntry.recycle(this.lastAccess);}}}PoolEntity:void recycle(long lastAccessed) {if (this.connection != null) {this.lastAccessed = lastAccessed;this.hikariPool.recycle(this);}}HikariPool:void recycle(PoolEntry poolEntry) {this.metricsTracker.recordConnectionUsage(poolEntry);this.connectionBag.requite(poolEntry);}ConcurrentBag:public void requite(T bagEntry) {bagEntry.setState(0);......}

最终执行concurrentBag.requite()方法,bagEntry.setState(0);

5. 关闭连接

5.1 关闭连接

关闭连接主要有两个逻辑:一是标记软驱逐,二是关闭连接。

时钟倒退,maxlifetime超时等都会进行软驱逐标记。

housekeeper的空闲连接超时,以及getConncection时对驱逐标记连接对象都可以执行关闭操作。

在这里插入图片描述

看一下关闭的公共方法closeConnection()

	void closeConnection(PoolEntry poolEntry, String closureReason) {//修改PoolEntry状态,从shareList和threadList中移除这个元素if (this.connectionBag.remove(poolEntry)) {//关闭MaxLifeTime超时检测任务,一些变量赋空值帮助GCConnection connection = poolEntry.close();//关闭任务提交到线程池,执行关闭this.closeConnectionExecutor.execute(() -> {this.quietlyCloseConnection(connection, closureReason);if (this.poolState == 0) {//如果连接池状态正常,维持最小连接this.fillPool();}});}}

http://chatgpt.dhexx.cn/article/2HEc0mio.shtml

相关文章

Hikari 讲解

前言 现在已经有很多公司在使用HikariCP了&#xff0c;HikariCP还成为了SpringBoot默认的连接池&#xff0c;伴随着SpringBoot和微服务&#xff0c;HikariCP 必将迎来广泛的普及。 下面带大家从源码角度分析一下HikariCP为什么能够被Spring Boot 青睐&#xff0c;文章目录如下…

SpringBoot系列十八:整合Hikari

Hikari是一款非常强大&#xff0c;高效&#xff0c;并且号称“史上最快连接池”。并且在springboot2.0之后&#xff0c;采用的默认数据库连接池就是Hikari。不需要引入依赖&#xff0c;已经在SpringBoot中包含了。  GitHub地址&#xff1a;https://github.com/brettwooldridge…

Android开发之RelativeLayout

文章目录 常见属性根据父容器定位根据兄弟容器定位 实例根据父容器定位根据兄弟组件定位 通用属性设置组件与父容器的边距设置父容器与组件的边距 常见属性 根据父容器定位 layout_alignParentStart 左对齐 layout_alignParentEnd 右对齐 layout_alignParentTop 顶部对齐 lay…

Android RelativeLayout布局

1. RelativeLayout类 相对布局&#xff08;RelativeLayout&#xff09;将子视图以相对位置显示。默认显示在父视图的左上角。 layout_alignParentTop&#xff0c;父视图的上边layout_alignParentBottom&#xff0c;父视图的下边layout_alignParentLeft&#xff0c;父视图的左…

android 继承relativelayout,Android开发中RelativeLayout相对布局

Android开发中RelativeLayout相对布局 RelativeLayout布局是Android界面布局中应用最广也最强大的一种布局&#xff0c;其不只十分灵活&#xff0c;能够解决开发中各类界面布局需求&#xff0c;同时也很方便了解决了多屏幕尺寸的适配问题。在iOS开发中&#xff0c;Autolayout技…

RelativeLayout实现居中偏下x距离引发的小问题

UE想实现一个简单的效果&#xff0c;某个控件在屏幕水平线下方50dp。由于受限于项目历史布局&#xff08;RelativeLayout&#xff09;和一套动态化设置控件位置的方案&#xff0c;竟然遇到了一点问题。&#xff08;在这里还是喊一声&#xff1a;ConstraintLayout最香&#xff0…

RelativeLayout布局

RelativeLayout布局是相对布局&#xff0c;如果RelativeLayout中再包含两个RelativeLayout&#xff0c;不会像LinearLayout一样&#xff0c;宽高一样的话会重叠在一起 将红色布局放到右上角 常见属性 根据父容器定位 layout_alignParentLeft 左对齐 layout_alignParentRig…

RelativeLayout圆角处理

RelativeLayout圆角处理以后&#xff0c;可以变相对子view进行圆角处理&#xff0c;如ImageView&#xff0c;VideoView等 RoundRelativeLayout具体实现 比较简单&#xff0c;只需要在初始化时设置一下layout的ViewOutlineProvider&#xff0c;方便起见&#xff0c;这里写死rad…

RelativeLayout(相对布局)的基本使用

RelativeLayout又称作相对布局&#xff0c;也是一种非常常用的布局。和LinearLayout的排列规则不同&#xff0c;RelativeLayout显得更加随意一些&#xff0c;它可以通过相对定位的方式让控件出现在布局的任何位置。也正因为如此&#xff0c;RelativeLayout中的属性非常多&#…

RelativeLayout相对布局详解

RelativeLayout相对布局在Android UI开发中也应用比较多&#xff0c;虽然它不及LinearLayout使用方便&#xff0c;但某些场景中使用RelativeLayout也是一种很不错的选择。在官网上介绍RelativeLayout是一个视图组&#xff0c;在相对位置显示子视图。RelativeLayout是用于设计用…

Android相对布局(RelativeLayout)

Android相对布局(RelativeLayout) 备注&#xff1a;这里的视图和元素是等同的概念。 RelativeLayout是一个允许子视图相对于其他兄弟视图或是父视图显示的视图组(通过ID指定)。每个视图的位置能够指定它相对于兄弟(比如在其他视图的左边或是下边)或是父视图(这里是指相对布局容…

【Android】相对布局(RelativeLayout)最全解析

【Android】相对布局&#xff08;RelativeLayout&#xff09;最全解析 一、相对布局&#xff08;RelativeLayout&#xff09;概述二、根据父容器定位三、根据兄弟控件定位 一、相对布局&#xff08;RelativeLayout&#xff09;概述 相对布局&#xff08;RelativeLayout&#x…

Flutter ListView详解

ListView详解 ListView常用构造ListViewListView 默认构建效果 ListView ListTileListTile 属性ListTile 使用效果 ListView builderbuilder属性详细介绍分析几个比较难理解的属性效果builder模式来设置分割线 ListView separatedseparatorBuilderseparated设置分割线效果 List…

UE4 ListView

UE4-ListView UE4ListView和U3D的思路不太一样&#xff0c;详细了解之后发现UE4的ListView还是蛮先进的&#xff0c;直接就实现了逻辑和显示分离&#xff0c;提高效率&#xff0c;相对的&#xff0c;他的用法也比Unity的ListView绕一些。举例&#xff0c;一个ListView如果设置…

Android 控件 —— ListView

ListView 的简单用法 在布局中加入 ListView 控件还算简单&#xff0c;先为 ListView 指定一个 id&#xff0c;然后将宽度和高度都设置为 match_parent&#xff0c;这样 ListView 就占满了整个布局的空间 <LinearLayout xmlns:android"http://schemas.android.com/ap…

ListView 组件

简介&#xff1a; ListView是最常用的可滚动组件之一 有三种构建方式&#xff1a; ListViewListView.builderListView.separated 主要参数说明&#xff1a; scrollDirection: Axis.horizontal 水平列表Axis.vertical 垂直列表 padding: 内边距 resolve: 组件反向排序 childr…

4.ListView

ListView 文章目录 ListView一、什么是ListView二、ListView入门1.ListView核心类2.代码编写步骤 三、ListView优化四、把复杂界面(通过xml文件实现)显示到ListView上1.View的静态方法2.获取LayoutInflater对象 五、SimpleAdapter & ArrayAdapter的使用1.ArrayAdapter2.Sim…

ListView及ListAdapter详解

ListView及ListAdapter详解 一、AdapterView 1. 简介 An AdapterView is a view whose children are determined by an Adapter. 简单地说就是其子视图是由适配器决定的视图组件 2. 子类 ListViewGridViewSpinnerGallery3. 常用方法 //功能:获取list中指定位置item get…

qt listview

运行图 .h #ifndef WIDGET_H #define WIDGET_H#include <QWidget>#include <QStringListModel> #include <QModelIndex>namespace Ui { class Widget; }class Widget : public QWidget {Q_OBJECTpublic:explicit Widget(QWidget *parent 0);~Widget();pri…

flutter 之 ListView的使用与详解 map for listview.builder 的使用

1.ListView 配合ListTile 实现新闻列表样式 ListView(children: <Widget>[ListTile(title: const Text(我是 title),subtitle: const Text(我是 sub_title),leading: Image.asset(images/c.png,fit: BoxFit.cover,),trailing: const Icon(Icons.chevron_right,size: 22,…