日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問(wèn) 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 编程资源 > 编程问答 >内容正文

编程问答

hikaril连接sql2000_hikari连接池解析(版本:HikariCP-2.5.1.jar)

發(fā)布時(shí)間:2023/12/10 编程问答 58 豆豆
生活随笔 收集整理的這篇文章主要介紹了 hikaril连接sql2000_hikari连接池解析(版本:HikariCP-2.5.1.jar) 小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

maxLifetime參數(shù)

maxLifetime參數(shù)必須小于數(shù)據(jù)庫(kù)的time_wait,默認(rèn)是1800000,即30分鐘。如果設(shè)置為0,表示存活時(shí)間無(wú)限大。如果不等于0且小于30秒則會(huì)被重置回30分鐘。HikariConfig類中有該參數(shù)的校驗(yàn)規(guī)則。

HikariPool類中,當(dāng)我們初始化連接池的時(shí)候,它的構(gòu)造方法中,實(shí)例化了this.POOL_ENTRY_CREATOR = new HikariPool.PoolEntryCreator();該類實(shí)現(xiàn)了Callable接口,用來(lái)初始化連接。

public Boolean call() throws Exception {

for(long sleepBackoff = 250L; HikariPool.this.poolState == 0 && HikariPool.this.totalConnections.get() < HikariPool.this.config.getMaximumPoolSize(); 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) {

HikariPool.this.totalConnections.incrementAndGet();

HikariPool.this.connectionBag.add(poolEntry);

return Boolean.TRUE;

}

UtilityElf.quietlySleep(sleepBackoff);

}

return Boolean.FALSE;

}

復(fù)制代碼

在其中調(diào)用createPoolEntry()生成一個(gè)連接。

private PoolEntry createPoolEntry() {

try {

final 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(new Runnable() {

public void run() {

HikariPool.this.softEvictConnection(poolEntry, "(connection has passed maxLifetime)", false);

}

}, lifetime, TimeUnit.MILLISECONDS));

}

this.LOGGER.debug("{} - Added connection {}", this.poolName, poolEntry.connection);

return poolEntry;

} catch (Exception var8) {

if (this.poolState == 0) {

this.LOGGER.debug("{} - Cannot acquire connection from data source", this.poolName, var8);

}

return null;

}

}

復(fù)制代碼

在該方法中,設(shè)置了一個(gè)延時(shí)任務(wù),具體的延時(shí)執(zhí)行時(shí)間是根據(jù)maxLifetime來(lái)計(jì)算,觸發(fā)時(shí)間距離maxlifetime的差值是根據(jù) maxLifetime > 10_000 ? ThreadLocalRandom.current().nextLong( maxLifetime / 40 ) : 0;

來(lái)計(jì)算(up to 2.5% of the maxlifetime),在連接存活將要到達(dá)maxLifetime之前觸發(fā)evit,用來(lái)防止出現(xiàn)大面積的connection因maxLifetime同一時(shí)刻失效。

當(dāng)被觸發(fā)時(shí),會(huì)標(biāo)記evict為true,標(biāo)記為evict只是表示連接池中的該連接不可用,但還在連接池當(dāng)中,還會(huì)被borrow出來(lái),只是getConnection的時(shí)候判斷了,如果是isMarkedEvicted,則會(huì)從連接池中移除該連接,然后close掉。

HikariCP中通過(guò)獨(dú)立的線程池closeConnectionExecutor進(jìn)行物理連接的關(guān)閉。出現(xiàn)以下三種情況時(shí)會(huì)觸發(fā)連接的自動(dòng)關(guān)閉:

連接斷開(kāi);

連接存活時(shí)間超出最大生存時(shí)間(maxLifeTime)

連接空閑時(shí)間超出最大空閑時(shí)間(idleTimeout)

closeConnectionExecutor關(guān)閉連接后,會(huì)調(diào)用fillPool()方法對(duì)連接池進(jìn)行連接填充

validationTimeout

validationTimeout用來(lái)指定驗(yàn)證連接有效性的超時(shí)時(shí)間(默認(rèn)是5秒,最小不能小于250毫秒),在HikariPool.getConnection方法中會(huì)調(diào)用isConnectionAlive(Connection connection)對(duì)連接進(jìn)行驗(yàn)證。

如果是jdbc4的話,可以使用isUseJdbc4Validation,是直接利用connection.isValid(validationSeconds)來(lái)驗(yàn)證連接的有效性;否則的話則用connectionTestQuery查詢語(yǔ)句來(lái)查詢驗(yàn)證。

leakDetectionThreshold`

該參數(shù)主要用來(lái)開(kāi)啟連接泄漏檢測(cè),在通過(guò)getConnection()獲取連接的時(shí)候,會(huì)繼續(xù)調(diào)用另外一個(gè)createProxyConnection()方法獲取連接,這里我們關(guān)注入?yún)his.leakTask.schedule(poolEntry)。

public final Connection getConnection(long hardTimeout) throws SQLException {

this.suspendResumeLock.acquire();

long startTime = clockSource.currentTime();

try {

long timeout = hardTimeout;

do {

PoolEntry poolEntry = (PoolEntry)this.connectionBag.borrow(timeout, TimeUnit.MILLISECONDS);

if (poolEntry == null) {

break;

}

long now = clockSource.currentTime();

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.leakTask.schedule(poolEntry), now);

return var10;

}

復(fù)制代碼

該schedule方法返回一個(gè)ProxyLeakTask對(duì)象

//返回 ProxyLeakTask

ProxyLeakTask schedule(PoolEntry bagEntry) {

return this.leakDetectionThreshold == 0L ? NO_LEAK : new ProxyLeakTask(this, bagEntry);

}

復(fù)制代碼

這里判斷l(xiāng)eakDetectionThreshold參數(shù)是否為0,默認(rèn)是0,不開(kāi)啟檢測(cè)。否則,就會(huì)開(kāi)啟一個(gè)延時(shí)執(zhí)行任務(wù),時(shí)間正好為設(shè)置的leakDetectionThreshold值,該任務(wù)的作用就是用來(lái)拋出Apparent connection leak detected異常。

private ProxyLeakTask(ProxyLeakTask parent, PoolEntry poolEntry) {

this.exception = new Exception("Apparent connection leak detected");

this.connectionName = poolEntry.connection.toString();

//延時(shí)執(zhí)行

this.scheduledFuture = parent.executorService.schedule(this, parent.leakDetectionThreshold, TimeUnit.MILLISECONDS);

}

復(fù)制代碼

截取一部分異常,如下

22:14:49.096 volte-cmd-service-test [HikariPool-1 housekeeper] WARN com.zaxxer.hikari.pool.ProxyLeakTask - Connection leak detection triggered for com.mysql.jdbc.JDBC4Connection@429fe922, stack trace follows

java.lang.Exception: Apparent connection leak detected

at org.hibernate.engine.jdbc.connections.internal.DatasourceConnectionProviderImpl.getConnection(DatasourceConnectionProviderImpl.java:122)

at org.hibernate.internal.AbstractSessionImpl$NonContextualJdbcConnectionAccess.obtainConnection(AbstractSessionImpl.java:386)

at org.hibernate.resource.jdbc.internal.LogicalConnectionManagedImpl.acquireConnectionIfNeeded(LogicalConnectionManagedImpl.java:87)

at org.hibernate.resource.jdbc.internal.LogicalConnectionManagedImpl.getPhysicalConnection(LogicalConnectionManagedImpl.java:112)

at org.hibernate.internal.SessionImpl.connection(SessionImpl.java:489)

at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)

at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)

at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)

at java.lang.reflect.Method.invoke(Method.java:497)

at org.springframework.util.ReflectionUtils.invokeMethod(ReflectionUtils.java:215)

at org.springframework.util.ReflectionUtils.invokeMethod(ReflectionUtils.java:200)

at org.springframework.orm.jpa.vendor.HibernateJpaDialect$HibernateConnectionHandle.doGetConnection(HibernateJpaDialect.java:414)

at org.springframework.orm.jpa.vendor.HibernateJpaDialect.beginTransaction(HibernateJpaDialect.java:177)

復(fù)制代碼

也就是從我們獲取這個(gè)連接開(kāi)始,到歸還連接之前的這一段時(shí)間,如果超過(guò)了leakDetectionThreshold,則會(huì)拋出上面的異常。

HouseKeeper

它是HikariPool中的一個(gè)內(nèi)部類,實(shí)現(xiàn)了Runnable接口,主要就是對(duì)連接進(jìn)行管理。在初始化HikariPool的時(shí)候,會(huì)創(chuàng)建一個(gè)scheduleWithFixedDelay任務(wù)(已固定延遲時(shí)間執(zhí)行,就是說(shuō)兩個(gè)任務(wù)之間的時(shí)間間隔是固定的,但每個(gè)任務(wù)的執(zhí)行時(shí)長(zhǎng)可能是不定的,與scheduleFixedRate的區(qū)別就是,不管任務(wù)是否執(zhí)行完,到點(diǎn)就執(zhí)行下一次任務(wù)),默認(rèn)30s執(zhí)行一次,刷新配置,進(jìn)行判斷。

public HikariPool(HikariConfig config) {

super(config);

this.ALIVE_BYPASS_WINDOW_MS = Long.getLong("com.zaxxer.hikari.aliveBypassWindowMs", TimeUnit.MILLISECONDS.toMillis(500L));

this.HOUSEKEEPING_PERIOD_MS = Long.getLong("com.zaxxer.hikari.housekeeping.periodMs", TimeUnit.SECONDS.toMillis(30L));

this.POOL_ENTRY_CREATOR = new HikariPool.PoolEntryCreator();

this.connectionBag = new ConcurrentBag(this);

this.totalConnections = new AtomicInteger();

this.suspendResumeLock = config.isAllowPoolSuspension() ? new SuspendResumeLock() : SuspendResumeLock.FAUX_LOCK;

this.checkFailFast();

if (config.getMetricsTrackerFactory() != null) {

this.setMetricsTrackerFactory(config.getMetricsTrackerFactory());

} else {

this.setMetricRegistry(config.getMetricRegistry());

}

this.setHealthCheckRegistry(config.getHealthCheckRegistry());

this.registerMBeans(this);

ThreadFactory threadFactory = config.getThreadFactory();

this.addConnectionExecutor = UtilityElf.createThreadPoolExecutor(config.getMaximumPoolSize(), this.poolName + " connection adder", threadFactory, new DiscardPolicy());

this.closeConnectionExecutor = UtilityElf.createThreadPoolExecutor(config.getMaximumPoolSize(), this.poolName + " connection closer", threadFactory, new CallerRunsPolicy());

//創(chuàng)建定時(shí)任務(wù)類

if (config.getScheduledExecutorService() == null) {

ThreadFactory threadFactory = threadFactory != null ? threadFactory : new DefaultThreadFactory(this.poolName + " housekeeper", true);

this.houseKeepingExecutorService = new ScheduledThreadPoolExecutor(1, (ThreadFactory)threadFactory, new DiscardPolicy());

//傳遞false參數(shù)給這個(gè)方法,執(zhí)行shutdown()方法之后,待處理的任務(wù)將不會(huì)被執(zhí)行。

this.houseKeepingExecutorService.setExecuteExistingDelayedTasksAfterShutdownPolicy(false);

//取消任務(wù)后,判斷是否需要從阻塞隊(duì)列中移除任務(wù)

this.houseKeepingExecutorService.setRemoveOnCancelPolicy(true);

} else {

this.houseKeepingExecutorService = config.getScheduledExecutorService();

}

this.leakTask = new ProxyLeakTask(config.getLeakDetectionThreshold(), this.houseKeepingExecutorService);

//初始化HouseKeeper

this.houseKeepingExecutorService.scheduleWithFixedDelay(new HikariPool.HouseKeeper(), 100L, this.HOUSEKEEPING_PERIOD_MS, TimeUnit.MILLISECONDS);

}

復(fù)制代碼

時(shí)間回?fù)芴幚?/p>

在HouseKeeper的run方法中,會(huì)先對(duì)時(shí)間進(jìn)行判斷。

這里主要就是通過(guò)一個(gè)時(shí)間差來(lái)判斷這個(gè)時(shí)間差返回內(nèi)是否有時(shí)間回?fù)?#xff0c;在初始化的時(shí)候會(huì)通過(guò)下面構(gòu)造方法生成一個(gè)時(shí)間戳

// previous=當(dāng)前時(shí)間-30s(默認(rèn)的定時(shí)任務(wù)間隔時(shí)間)

private HouseKeeper() {

this.previous = HikariPool.clockSource.plusMillis(HikariPool.clockSource.currentTime(), -HikariPool.this.HOUSEKEEPING_PERIOD_MS);

}

復(fù)制代碼

當(dāng)初始化完成的第一次30s后或者上次任務(wù)執(zhí)行完的30s后,再執(zhí)行該任務(wù),如果當(dāng)前的時(shí)間戳+128ms還要小于previous(上次執(zhí)行后減去30s的時(shí)間戳)+30s,則表示有時(shí)間回?fù)?/p>

//檢測(cè)逆行時(shí)間,根據(jù)NTP規(guī)范允許+128ms

if (HikariPool.clockSource.plusMillis(now, 128L) < HikariPool.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, HikariPool.clockSource.elapsedDisplayString(this.previous, now));

this.previous = now;

HikariPool.this.softEvictConnections();

HikariPool.this.fillPool();

return;

}

復(fù)制代碼

此時(shí),會(huì)打印日志,并重置previous為當(dāng)前時(shí)間,設(shè)置連接為不可用,再重新生成連接。

保持最小連接

如果時(shí)間沒(méi)有錯(cuò)誤,則會(huì)判斷idleTimeout,如果大于0,取出當(dāng)前空閑連接

判斷是否大于最小連接數(shù)minimumIdle,如果大于,則繼續(xù)對(duì)當(dāng)前的空閑連接基于lastAccessed(最后一次訪問(wèn)時(shí)間)進(jìn)行排序,再遍歷

如果取出的每個(gè)連接的空閑時(shí)間已經(jīng)超過(guò)了idleTimeout,并且成功將連接從NOT_IN_USE(閑置中)更改為RESERVED(標(biāo)記為保留中)

則關(guān)閉該連接

最后再新創(chuàng)建連接

public void run() {

try {

//刷新connectionTimeout、validationTimeout

HikariPool.this.connectionTimeout = HikariPool.this.config.getConnectionTimeout();

HikariPool.this.validationTimeout = HikariPool.this.config.getValidationTimeout();

HikariPool.this.leakTask.updateLeakDetectionThreshold(HikariPool.this.config.getLeakDetectionThreshold());

long idleTimeout = HikariPool.this.config.getIdleTimeout();

long now = HikariPool.clockSource.currentTime();

//時(shí)鐘回?fù)芘袛?/p>

if (HikariPool.clockSource.plusMillis(now, 128L) < HikariPool.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, HikariPool.clockSource.elapsedDisplayString(this.previous, now));

this.previous = now;

HikariPool.this.softEvictConnections();

HikariPool.this.fillPool();

return;

}

if (now > HikariPool.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, HikariPool.clockSource.elapsedDisplayString(this.previous, now));

}

this.previous = now;

String afterPrefix = "Pool ";

if (idleTimeout > 0L) {

//取出空閑連接 連接狀態(tài),IN_USE(1:使用中)、NOT_IN_USE(0:閑置中)、REMOVED(-1:已移除)、RESERVED(-1:標(biāo)記為保留中)

List idleList = HikariPool.this.connectionBag.values(0);

int removable = idleList.size() - HikariPool.this.config.getMinimumIdle();

if (removable > 0) {

HikariPool.this.logPoolState("Before cleanup ");

afterPrefix = "After cleanup ";

//排序

idleList.sort(PoolEntry.LASTACCESS_COMPARABLE);

Iterator var8 = idleList.iterator();

while(var8.hasNext()) {

PoolEntry poolEntry = (PoolEntry)var8.next();

//idleTimeout判斷,連接狀態(tài)修改

if (HikariPool.clockSource.elapsedMillis(poolEntry.lastAccessed, now) > idleTimeout && HikariPool.this.connectionBag.reserve(poolEntry)) {

HikariPool.this.closeConnection(poolEntry, "(connection has passed idleTimeout)");

--removable;

if (removable == 0) {

break;//keep min idle cons

}

}

}

}

}

HikariPool.this.logPoolState(afterPrefix);

HikariPool.this.fillPool();

} catch (Exception var10) {

HikariPool.this.LOGGER.error("Unexpected exception in housekeeping task", var10);

}

}

}

復(fù)制代碼

問(wèn)題

minimumIdle不一致問(wèn)題

當(dāng)前版本在應(yīng)用初始化的時(shí)候,連接池也會(huì)進(jìn)行初始化,但是當(dāng)我們配置的數(shù)據(jù)源屬性minimumIdle

//HikariCP-3.4.5.jar

private synchronized void fillPool()

{

final int connectionsToAdd = Math.min(config.getMaximumPoolSize() - getTotalConnections(), config.getMinimumIdle() - getIdleConnections())

- addConnectionQueueReadOnlyView.size();

if (connectionsToAdd <= 0) logger.debug("{} - Fill pool skipped, pool is at sufficient level.", poolName);

//生成的個(gè)數(shù)減去了1

for (int i = 0; i < connectionsToAdd; i++) {

addConnectionExecutor.submit((i < connectionsToAdd - 1) ? poolEntryCreator : postFillPoolEntryCreator);

}

}

//HikariCP-2.5.1.jar 的該方法

for (int i = 0; i < connectionsToAdd; i++) {

addBagItem();

}

復(fù)制代碼

超時(shí)問(wèn)題

錯(cuò)誤日志如下:

java.sql.SQLTransientConnectionException: HikariPool-1 - Connection is not available, request timed out after 30000ms.

復(fù)制代碼首先檢查配置是否有問(wèn)題,maxLifetime不能大于數(shù)據(jù)庫(kù)的time_wait,查詢mysql配置show variables like ‘%timeout%’,默認(rèn)為8小時(shí)。

配置沒(méi)有問(wèn)題,則有可能與HikariCP無(wú)關(guān)。這個(gè)錯(cuò)誤的產(chǎn)生原因就是請(qǐng)求向池中borrow時(shí),沒(méi)有可用連接超時(shí)導(dǎo)致。第一點(diǎn),此時(shí)我們要思考我們的連接池?cái)?shù)量設(shè)置是否合理,與業(yè)務(wù)量相關(guān);第二點(diǎn),看我們代碼是否存在慢sql;第三點(diǎn),與使用的持久層框架有關(guān),分析我們的連接到底是被誰(shuí)所持有,它的連接管理方法是怎么樣的,什么情況下才會(huì)歸還連接。

這篇文章就是因?yàn)槲矣龅竭@個(gè)錯(cuò)而無(wú)法定位才決定好好研究下的,我的這個(gè)錯(cuò)誤產(chǎn)生的原因就是上面的第三點(diǎn),我的項(xiàng)目采用的是jpa做數(shù)據(jù)庫(kù)交互,且是一個(gè)非常簡(jiǎn)單的單表查詢,連接應(yīng)該很快歸還池中才對(duì),但是經(jīng)過(guò)我的測(cè)試,當(dāng)經(jīng)過(guò)數(shù)據(jù)庫(kù)查詢以后,連接并沒(méi)有被釋放,反而是在我的整個(gè)會(huì)話結(jié)束后,才會(huì)歸還連接。

jpa的核心是hibernate-core,在網(wǎng)上查詢了hibernate的連接釋放策略,知道了原因。hibernate 中連接釋放的策略hibernate. connection. release_ mode有以下四種屬性:

on_close,當(dāng)Session被顯式關(guān)閉或被斷開(kāi)連接時(shí),才會(huì)釋放JDBC連接

after_transaction,每次事務(wù)結(jié)束都會(huì)釋放鏈接

after_statement,在每次JDBC調(diào)用后,都會(huì)主動(dòng)的釋放連接

auto,為JTA和CMT事務(wù)策略選擇after_statement, 為JDBC事務(wù)策略選擇after_transaction

我的springboot項(xiàng)目版本為1.x,即便將hibernate-core升級(jí)到較高版本,并開(kāi)啟事物,也還是基于on_close方式去釋放連接;我測(cè)試了2.x版本的,不開(kāi)啟事物時(shí)也是on_close,開(kāi)啟事物后,就成了after_transaction,具體1.x版本為何開(kāi)啟事物也不生效還不清楚。

參考資料

流程圖的方式講解,很清晰易懂,版本也比較高

HikariCP是如何管理數(shù)據(jù)庫(kù)連接的?

關(guān)于找一找教程網(wǎng)

本站文章僅代表作者觀點(diǎn),不代表本站立場(chǎng),所有文章非營(yíng)利性免費(fèi)分享。

本站提供了軟件編程、網(wǎng)站開(kāi)發(fā)技術(shù)、服務(wù)器運(yùn)維、人工智能等等IT技術(shù)文章,希望廣大程序員努力學(xué)習(xí),讓我們用科技改變世界。

[hikari連接池解析(版本:HikariCP-2.5.1.jar)]http://www.zyiz.net/tech/detail-143838.html

總結(jié)

以上是生活随笔為你收集整理的hikaril连接sql2000_hikari连接池解析(版本:HikariCP-2.5.1.jar)的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。

如果覺(jué)得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。