天天看點

hikari配置斷開重連_聊聊hikari連接配接池的fixed pool design

本文主要研究一下hikari連接配接池的fixed pool design

fixed pool design

hikari的作者比較傾向于fixed pool design的理念,即建議minimumIdle與maximumPoolSize設定成一樣,當做固定連接配接大小的連接配接池。作者認為minimumIdle小于maximumPoolSize的話,在流量激增的時候需要額外的連接配接,此時在請求方法裡頭再去處理建立連接配接會造成性能損失,即會導緻資料庫一方面降低連接配接建立的速度,另一方面也會影響既有的連接配接事務的完成,間接影響了這些既有連接配接歸還到連接配接池的速度。

作者認為minimumIdle與maximumPoolSize設定成一樣,多餘的空閑連接配接不會對整體的性能有什麼嚴重影響,如果說設定minimumIdle小于maximumPoolSize是為了在必要的時候可以釋放連接配接以釋放記憶體給其他功能用,但是在高峰時期,連接配接池可能也會到達maximumPoolSize,因而這個目的似乎沒起到效果。

HikariPool.houseKeeperTask

HikariCP-2.7.6-sources.jar!/com/zaxxer/hikari/pool/HikariPool.java

private final long HOUSEKEEPING_PERIOD_MS = Long.getLong("com.zaxxer.hikari.housekeeping.periodMs", SECONDS.toMillis(30));

public HikariPool(final HikariConfig config)

{

super(config);

this.connectionBag = new ConcurrentBag<>(this);

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

this.houseKeepingExecutorService = initializeHouseKeepingExecutorService();

checkFailFast();

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

setMetricsTrackerFactory(config.getMetricsTrackerFactory());

}

else {

setMetricRegistry(config.getMetricRegistry());

}

setHealthCheckRegistry(config.getHealthCheckRegistry());

registerMBeans(this);

ThreadFactory threadFactory = config.getThreadFactory();

LinkedBlockingQueue addConnectionQueue = new LinkedBlockingQueue<>(config.getMaximumPoolSize());

this.addConnectionQueue = unmodifiableCollection(addConnectionQueue);

this.addConnectionExecutor = createThreadPoolExecutor(addConnectionQueue, poolName + " connection adder", threadFactory, new ThreadPoolExecutor.DiscardPolicy());

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

this.leakTaskFactory = new ProxyLeakTaskFactory(config.getLeakDetectionThreshold(), houseKeepingExecutorService);

this.houseKeeperTask = houseKeepingExecutorService.scheduleWithFixedDelay(new HouseKeeper(), 100L, HOUSEKEEPING_PERIOD_MS, MILLISECONDS);

}

在初始化HikariPool的時候會初始化houseKeeperTask

HikariPool.HouseKeeper

HikariCP-2.7.6-sources.jar!/com/zaxxer/hikari/pool/HikariPool.java

private final class HouseKeeper implements Runnable

{

private volatile long previous = plusMillis(currentTime(), -HOUSEKEEPING_PERIOD_MS);

@Override

public void run()

{

try {

// refresh timeouts in case they changed via MBean

connectionTimeout = config.getConnectionTimeout();

validationTimeout = config.getValidationTimeout();

leakTaskFactory.updateLeakDetectionThreshold(config.getLeakDetectionThreshold());

final long idleTimeout = config.getIdleTimeout();

final long now = currentTime();

// Detect retrograde time, allowing +128ms as per NTP spec.

if (plusMillis(now, 128) < plusMillis(previous, HOUSEKEEPING_PERIOD_MS)) {

LOGGER.warn("{} - Retrograde clock change detected (housekeeper delta={}), soft-evicting connections from pool.",

poolName, elapsedDisplayString(previous, now));

previous = now;

softEvictConnections();

return;

}

else if (now > plusMillis(previous, (3 * HOUSEKEEPING_PERIOD_MS) / 2)) {

// No point evicting for forward clock motion, this merely accelerates connection retirement anyway

LOGGER.warn("{} - Thread starvation or clock leap detected (housekeeper delta={}).", poolName, elapsedDisplayString(previous, now));

}

previous = now;

String afterPrefix = "Pool ";

if (idleTimeout > 0L && config.getMinimumIdle() < config.getMaximumPoolSize()) {

logPoolState("Before cleanup ");

afterPrefix = "After cleanup ";

final List notInUse = connectionBag.values(STATE_NOT_IN_USE);

int toRemove = notInUse.size() - config.getMinimumIdle();

for (PoolEntry entry : notInUse) {

if (toRemove > 0 && elapsedMillis(entry.lastAccessed, now) > idleTimeout && connectionBag.reserve(entry)) {

closeConnection(entry, "(connection has passed idleTimeout)");

toRemove--;

}

}

}

logPoolState(afterPrefix);

fillPool(); // Try to maintain minimum connections

}

catch (Exception e) {

LOGGER.error("Unexpected exception in housekeeping task", e);

}

}

}

假設minimumIdle與maximumPoolSize設定成一樣,那麼這個task在第一次執行的時候,直接執行fillPool

fillPool

private synchronized void fillPool()

{

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

- addConnectionQueue.size();

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

addConnectionExecutor.submit((i < connectionsToAdd - 1) ? POOL_ENTRY_CREATOR : POST_FILL_POOL_ENTRY_CREATOR);

}

}

這個fillPool,在初始化時刻,minimumIdle與maximumPoolSize值一樣,totalConnections與idleConnections都為0,那麼connectionsToAdd的值就是maximumPoolSize

也就是說這個task會添加maximumPoolSize大小連接配接

小結

tomcat jdbc pool

有個initial-size參數來指定最開始的時候初始化多少個連接配接,有min-idle及max-idle來控制空閑連接配接的最小值及最大值,有max-active來控制連接配接池總大小。min-evictable-idle-time-millis用來指定空閑連接配接的時長,time-between-eviction-runs-millis用來指定清理空閑連接配接的任務的排程時間間隔。

hikari connection pool

有minIdle來指定空閑連接配接的最小數量,maxPoolSize指定連接配接池連接配接最大值,預設初始化的時候,是初始化minIdle大小的連接配接,如果minIdle與maxPoolSize值相等那就是初始化時把連接配接池填滿。idleTimeout用來指定空閑連接配接的時長,maxLifetime用來指定所有連接配接的時長。com.zaxxer.hikari.housekeeping.periodMs用來指定連接配接池空閑連接配接處理及連接配接池數補充的HouseKeeper任務的排程時間間隔。

也就是說hikari比tomcat jdbc pool多了個maxLifetime,也就是所有的連接配接在maxLifetime之後都得重連一次,保證連接配接池的活性。

doc