1.この章の目的
リソース共有元の状態と同様の状態変化を理解する方法、デザインの参考のためにリソースをプールします。
2.HikariPoolリソースのコアクラスレビュー
次のようにHikariPool資源関連のクラスは次のとおりです。
カテゴリ | 責任 |
---|---|
HikariPool | リソースプール、入り口クライアントリソース操作。 |
ConcurrentBag | GMとの契約ツール。 |
CopyOnWriteArrayListと | PoolEntryあるストレージのためのリソースのリスト、。その特徴は、並行性を向上させるために、ロック、書き込みロックを、読んでいません。 |
プールエントリ | リソースパッケージ、パッケージ・コネクション、このクラスのリソースレコードの状態。 |
接続 | 本当にリソース、データベース接続を適用したいです。 |
3.リソースのステータス
すなわち、PoolEntryの状態や、実際に二つの属性:
3.1状態
リソースを宣言する状態は以下のようにその状態が変化し、提供されています:
状態 | ときに変更します |
---|---|
使用されていません | 1.接続プールが初期化されている プールに2リリースリソースバック 3.貸出リソースいないときに十分なリソース |
使用中で | 利用可能なリソースを貸します。 |
予約 | 1.リソースが最大のライフサイクル越え 2.資源プールのシャットダウンを するときレトログラード時間3.Detect 4と呼ばれる、発信者の取り組み。 |
削除されました | 1.接続プールの初期化、動的ストレッチ(落下時間)が 接続取得2. 3.特定のエラーが発生しました。 |
3.1.1 NOT INを使用します
説明状態リソースは、単に初期化またはリリースのリソースがリソースプールにバックアップ状態になるだろうされているリソースの割り当てを待って、使用されていません。
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.发生特定异常时,这类异常都是直接导致数据库连接不可用的异常,因此会将数据库连接释放掉。
3.2 evict
资源的另外一个状态或者属性是evict标志,如果标志为true,就意味着资源不可用。其状态变化比较简单,默认为false。
这个标记最主要的作用是在获取资源时,如果资源的evict标志为true,则这个资源不可用,会接着获取下一个资源。
资源变为true的时机可参考state变为removed的时机,可以理解为就是资源不可用,将要清理并释放掉的时候。
从上面两个状态的变化来看,似乎用removed状态可以替代evict为true,但是在HikariPool中并没有这么做,一方面可能是因为从业务上讲两者的业务含义不同,另外一方面evict还用在异常处理中,对于异常的处理这里不再深入展开,有兴趣可以看下源码。
4. 总结
HikariPool使用了资源状态来控制资源是否可用,而不是通过一个可用资源池和一个已用资源池来控制资源可用,这么做的好处有:
- 控制粒度更小,更精确,不需要在池上加锁,只要在具体资源上加锁,符合并发编程优化的减小锁粒度原则
- 扩展更容易,如果通过不同的池来控制,那么增加新的状态,会导致要增加新的池来记录这些资源。
HikariPool使用了state和evict来控制资源的使用,实际设计时是否需要如此,要结合实际情况来看:
- 从解耦的角度看,如果业务上是两个业务语义,并且不同语义有不同用途,那么就分开。
- 如果实际业务只需要一个state就够了,就没有必要一开始就拆分为两个,可遵循适用原则,等有需要时再扩展。
end.
<--感谢三连击,左边点赞和关注。
相关阅读:
HikariPool源码(一)初识
HikariPool源码(二)设计思想借鉴
HikariPool源码(三)资源池动态伸缩
Javaのオタクサイト: javageektour.com/