网站首页 > 精选文章 / 正文
内容简介:HikariCP源码解析
背景:springboot2.4+HikariCP 3.4.5
二、源码分析
咱们书接上回,继续探寻HikariCP的奥秘,首先看下HikariCP的获取连接时序图
上回我们分析了HikariDataSource的getConnection()方法,而这个方法的实现细节都是在HikariPool的getConnection()方法中,接下来就要看看HikariPool的getConnection()方法
1-HikariPool的getConnection方法
我们一点点看
(1)申请令牌
suspendResumeLock.acquire();其作用是当连接池挂起的时候, 控制用户不能获取连接。
比如我们通过JMX修改在运行中的HikariPool的参数时,就需要挂起连接池,将原来所有的连接都从连接池中驱逐出去,再恢复连接池,这时候的连接池才会使用新的配置创建新的连接。
suspendResumeLock类是用Semaphore实现的。Semaphore是java.util.concurrent.包下的 并发工具类,它用给线程发放令牌的方式,控制线程的并发数量。HikariCP 在这里初始化了 1万个令牌,挂起连接池,其实就是调用了Semaphore一次获取 1 万个令牌,这样其他线程就没有令牌可以拿了,只能等待,直到用户恢复线程池,释放这 1 万个令牌到桶里。需要注意的是,要使用挂起连接池的功能,必须配置isAllowPoolSuspension=true,否则使用挂起功能会报错。
(2)获取连接
调用ConcurrentBag的borrow方法借用一个PoolEntry对象,对于连接池而言实际就是从连接池中获取一个连接,connectionBag是 HikariCP中实际保存数据库连接的容器,里面是一个CopyOnWriteArrayList
核心步骤只有两步,
borrow() 的主要逻辑是:
查看线程本地存储 threadList 中是否有空闲连接,如果有,则返回一个空闲的连接;
如果线程本地存储中无空闲连接,则从共享队列 sharedList 中获取;
如果共享队列中也没有空闲的连接,则请求线程需要等待。
(3)获取连接后判断条件
如果connectionBag给我们返回了一个连接,那么需要判断两个条件:
1-该连接是否被软驱逐(标记状态)了,poolEntry.isMarkedEvicted()
2-计算连接到现在多长时间没有被使用过了(界限默认是500ms)且该连接是否已经不可用了!isConnectionAlive(poolEntry.connection)
这么做的原因是HikariCP的连接不是实时从连接池里剔除的,只是给连接上打个标记而已,都是在获取连接的时候检查是否可用,如果不可用的时候才直接从连接池里删除。
这里涉及到一个知识点,连接跟数据库通信有两种方式:
JDBC4 以下版本的驱动,使用用户配置的connectionTestQuery中的 sql 来检查。 connectionTestQuery是获取连接的时候,用于检查连接是否可用的一个 sql,大家可能用过,常见的是配置一个select 1。
JDBC4 以上,如果不配置connectionTestQuery, 默认使用 ping 命令检查。
如果使用的是 JDBC4 以上的驱动,建议大家不用配置connectionTestQuery,因为 ping 命令的方式比执行一个 sql 要高效很多。
Case1:若连接不用,则关闭连接
首先会把这个连接对象从ConnectionBag里移除,然后把实际的物理连接交给一个线程池去异步执行,这个线程池就是closeConnectionExecutor,然后异步任务内开始实际的关连接操作,因为主动关闭了一个连接相当于少了一个连接,所以还会触发一次扩充连接池操作。
Case2:若连接可用,
1-上报监控平台,
metricsTracker.recordBorrowStats,记录连接的借用
2-创建代理连接,
poolEntry.createProxyConnection,动态生成代理connection对象。
这里有个疑问就是从连接池获取的连接都是java.sql.Connection类型的对象,但是这里为什么要建立动态代理呢?原因是如果直接返回底层的数据库连接给用户使用,那么,如果用户自己关闭了这个底层数据库连接,那么这个连接在连接池里就不可用了,其他线程也使用不了了,这样就失去了连接池的意义了。所以就设计了一个createProxyConnection方法来创建了一个连接的代理ProxyConnection,将这个代理返回给用户使用。ProxyConnection继承了java.sql.Connection,覆盖了一些方法,比如关闭连接,如果用户调用了关闭连接操作,不是真正的关闭底层连接,而是将连接还回到连接池。
2-泄露检测的定时任务
在createProxyConnection方法中,参数leakTask.schedule(poolEntry),leakTask的类型是ProxyLeakTaskFactory,其用于泄露检测
(4)连接超时
此处的connectionTimeout就是我们在yaml文件中配置的参数,他是在初始化连接池的时候就获取到值了,默认是30s
我们看到(2)和(3)都是在不超时的dowhile循环中执行,一旦超时,会有两个步骤:一是向监控平台上报获取连接超时;二是构造一个异常信息,然后抛出去。
(5)释放锁
我们在最初申请了令牌,最后就需要释放锁,为了防止任何异常打断代码执行,所以释放锁的代码一定要放在 finally 中,保证最后一定会把锁释放掉。
至此,整个获取连接的逻辑就介绍完了。
2-HikariPool的初始化
其中,主要包含两部分,
一是调用HikariPool对象的父类对象PoolBase的构造器(读取HikariConfig配置信息配置PoolBase的属性和调用PoolBase的构造器的initializeDataSource方法)
二是配置一堆线程池信息
我们一点点看
1-初始化父类PoolBase
super(config)作用是初始化父类PoolBase中的数据库配置,通过HikariConfig逐层初始化相关的配置。PoolBase是一个更接近底层的一个连接池抽象类。它里面定义了一些数据库连接相关的配置,比如:是否自动提交事务,是否使用 JDBC4等。以及初始化 JDBC 的dataSource,验证连接是否存活,重置连接默认配置等等。
2-初始化ConcurrentBag
ConcurrentBag就是并发包,本质就是连接池的主体,它的内部是一个CopyOnWriteArrayList,用于保存连接。存储连接的封装对象PoolEntry,另外做了并发控制来解决连接池的并发问题。
3-初始化suspendResumeLock
就是创建连接池挂起的锁,这个地方在上一篇文章中有所介绍,此处不再赘述
4-初始化
houseKeepingExecutorService
这是一个定时线程池,默认只有一个线程,它的作用比较多:用于执行检测连接泄露、关闭生存时间到期的连接、回收空闲连接、检测时间回拨。
5-快速检测失败
在初始化 HikariCP 的时候,建立一个连接,然后立即关闭,如果有报错建立不了,就关闭整个连接池,抛错
6-监控设置
上篇文章我们已经知道获取连接的时候,会向监控平台上报自己的状态,这里就是初始化监控平台的相关配置
8-注册MBean
这里是注册 JMX 相关的 MBean,只有配置了数据库的isRegisterMbeans配置项,HikariCP 才会注册MBean,我们才能使用 JMX 在运行期间修改连接池的配置。如果不配置isRegisterMbeans,那么使用 JMX 修改配置会报错。
9-初始化线程池
closeConnectionExecutor :用于执行关闭底层连接的线程池,只有一个线程,线程任务队列最大是连接池最大连接数,超出队列的任务,会不断重试添加。
addConnectionExecutor:用于执行添加新连接的线程池,只有一个线程,线程任务队列最大是连接池最大连接数,超出队列的任务,直接抛弃。
10-启动连接管理任务
这里向
houseKeepingExecutorService线程池里提交了一个任务:每隔 30 秒,就执行一次HouseKeeper任务。这个任务的功能主要是:检测时间回拨,调整连接池里的连接。什么是时间回拨?比如服务器的系统时间不准,后来用户修改了服务器的系统时间,因为 HikariCP 是对时间敏感的框架,它靠定时任务来管理连接,如果系统时间变了,那么定时任务就不准确了。
11-创建连接泄露检测任务的父任务
用户获取到每个连接的时候,都会为该连接创建一个连接泄露检测的定时任务,在指定的时间内,抛出连接泄露警告。
在创建连接泄露检测任务的时候,会使用一个父任务的参数,从这个父任务中拿连接泄露的最大时间和用于执行任务的线程池,然后使用这两个参数创建任务。这个父任务,就是在这里创建的,创建的时候就是传了这两个参数:连接泄露的最大时间和用于执行任务的线程池。
以上就是对HikariPool初始化的介绍。
3-HikariCP的指标监控
HikariCP提供了一些监控指标,他的监控指标都是基于MicroMeter提供出来的,然后支持Prometheus和Dropwizard。本次我们将讨论一下HikariCp的监控指标有哪些,对于参数的监控我们以后会专门写篇文章进行介绍,此处就不先介绍了。
至此我们对于HikariCP的原理有了初步的了解,还有很多地方是值得慢慢思考设计者的设计思想的,静下心来慢慢看源码,你就会慢慢发现设计者思想的伟大之处,还是要慢慢学习。
参考文档:
https://github.com/brettwooldridge/HikariCP (官方文档)
https://www.jianshu.com/nb/46657191
Tags:hikari连接池
猜你喜欢
- 2025-06-30 爆肝 30 天!从 JVM 调优到百万级 QPS,我的 Java 性能飞升全记录(2)
- 2025-06-30 Spring Boot 连接 MySQL 数据库(spring boot 链接数据库)
- 2025-06-30 MyBatis配置详解:从入门到精通(mybatis如何配置)
- 2025-06-30 爆肝 30 天!从 JVM 调优到百万级 QPS,我的 Java 性能飞升全记录(1)
- 2025-06-30 每天从外包系统同步百万数据,用什么方案?Java实战讲解
- 2025-06-30 同步 vs 异步性能差100倍!SpringBoot3 高吞吐接口实现终极方案
- 2025-06-30 【耗时一月】AWS Aurora 数据库 Failover 处理方案
- 2025-06-30 SpringBoot 开发者必看!这 10 个内置神器让开发效率翻倍!
- 2025-06-30 如何避免数据库雪崩?高并发下的性能挑战与应对!
- 2025-06-30 Java语言的智能名片系统源码,二次开发流程