了解池资源的状态,以及状态如何变迁,用于池化资源设计参考。
HikariPool资源相关的类如下:
类说明:
类 | 职责 |
---|---|
HikariPool | 资源池,客户端资源操作的入口。 |
ConcurrentBag | 通用的并发包工具。 |
CopyOnWriteArrayList | 一个列表,用于存储资源,也就是PoolEntry。其特点是读不加锁,写操作加锁,提高并发性能。 |
PoolEntry | 资源封装类,封装了Connection,资源的状态记录在这个类上。 |
Connection | 真正要适用的资源,数据库连接。 |
在PoolEntry上实际有两个状态或者说属性,分别是:
state用于声明资源是否可用,其状态变化如下:
状态 | 何时变化 |
---|---|
Not In Use | 1.连接池初始化时 2.释放资源回池时 3.出借资源,资源不够时 |
In Use | 出借可用资源时。 |
Reserved | 1.资源超过最大生命周期时 2.资源池shutdown时 3.Detect retrograde time时 4.调用者主动调用时。 |
Removed | 1.连接池初始化,动态伸缩时(降时) 2.获取连接时 3.发生特定异常时。 |
3.1.1 Not In Use
这个状态说明资源没有被使用,等待分配,资源刚初始化好或者释放回资源池中的资源将变成这个状态。
1.连接池初始化时,连接池中的资源初始化并达到最小资源数,这些初始化的资源就是这个状态。
2.资源使用完释放会池中时,资源状态会从In Use变成Not In Use。 3.出借资源,资源不足时,此时如果池中的资源数没有达到最大资源数,则会创建新的资源,新资源状态也是这个状态。
3.1.2 In Use
唯一的场景就是出借资源后,资源从Not In Use变成In Use。
3.1.3 Reserved
1.资源超过最大生命周期时是指:每一个资源实例可以设置最大生命周期,如果超过最大生命周期还没有使用(Not In Use)则会调整为Reserved状态,这个状态下资源不能被使用。
2.资源池shoudown时也会先将资源的状态从Not In Use修改为Reserved状态,避免再被出借出去。
3.Detect retrograde time时,是一个很特殊的场景,这是检查时钟同步时是否回拨了,这个场景我们一般不会考虑到,参考代码如下:
//HouseKeeper.java // Detect retrograde time, allowing +128ms as per NTP spec. if (plusMillis(now, 128) < plusMillis(previous, housekeepingPeriodMs)) { logger.warn("{} - Retrograde clock change detected (housekeeper delta={}), soft-evicting connections from pool.", poolName, elapsedDisplayString(previous, now)); previous = now; softEvictConnections(); return; } 复制代码
资源什么时候超过最大生命周期,是通过一个延迟的线程任务来执行,如果线程执行了,资源还没有被使用,说明超过了最大生命周期。
4.允许调用者主动调用方法来设置这个状态,HikariPool中没有直接调用点。代码如下:
//HikariDataSource.java /** * Evict a connection from the pool. If the connection has already been closed (returned to the pool) * this may result in a "soft" eviction; the connection will be evicted sometime in the future if it is * currently in use. If the connection has not been closed, the eviction is immediate. * * @param connection the connection to evict from the pool */ public void evictConnection(Connection connection) { HikariPool p; if (!isClosed() && (p = pool) != null && connection.getClass().getName().startsWith("com.zaxxer.hikari")) { p.evictConnection(connection); } } 复制代码
3.1.4 Removed
这个状态意味着资源从连接池中清除,数据库连接被真正关闭,资源被释放。
1.连接池初始化,动态伸缩时(降时),这是指资源数量超过最小连接数,并且部分连接的闲置时间也已经超过允许的闲置时间,那么就会释放这些连接,使得池中的资源数量降到最小连接数。
2.获取连接时,通常我们不可能在此时去释放连接,这里是因为当连接池shutdown时,有的连接可能被设置为evicted,这样的连接是不可用的,对这样的连接要释放掉,导致这个场景发生的原因这两个动作没有加锁,而不加锁的目的是为了提升性能,毕竟这种场景并不多见,大多数时候不会出现,也就在大多数情况下提升了性能。
3.发生特定异常时,这类异常都是直接导致数据库连接不可用的异常,因此会将数据库连接释放掉。
资源的另外一个状态或者属性是evict标志,如果标志为true,就意味着资源不可用。其状态变化比较简单,默认为false。
这个标记最主要的作用是在获取资源时,如果资源的evict标志为true,则这个资源不可用,会接着获取下一个资源。
资源变为true的时机可参考state变为removed的时机,可以理解为就是资源不可用,将要清理并释放掉的时候。
从上面两个状态的变化来看,似乎用removed状态可以替代evict为true,但是在HikariPool中并没有这么做,一方面可能是因为从业务上讲两者的业务含义不同,另外一方面evict还用在异常处理中,对于异常的处理这里不再深入展开,有兴趣可以看下源码。
HikariPool使用了资源状态来控制资源是否可用,而不是通过一个可用资源池和一个已用资源池来控制资源可用,这么做的好处有:
HikariPool使用了state和evict来控制资源的使用,实际设计时是否需要如此,要结合实际情况来看:
end.
<--感谢三连击,左边点赞和关注。