本文主要研究一下hikari鏈接池的maxLifetime屬性及evict操做java
用來設置一個connection在鏈接池中的存活時間,默認是1800000,即30分鐘。若是設置爲0,表示存活時間無限大。若是不等於0且小於30秒則會被重置回30分鐘。
用來標記鏈接池中的鏈接不可用,這樣在borrow鏈接的時候,若是是標記evict的,則會繼續獲取鏈接git
/** * Get a connection from the pool, or timeout after the specified number of milliseconds. * * @param hardTimeout the maximum time to wait for a connection from the pool * @return a java.sql.Connection instance * @throws SQLException thrown if a timeout occurs trying to obtain a connection */ public Connection getConnection(final long hardTimeout) throws SQLException { suspendResumeLock.acquire(); final long startTime = currentTime(); try { long timeout = hardTimeout; do { PoolEntry poolEntry = connectionBag.borrow(timeout, MILLISECONDS); if (poolEntry == null) { break; // We timed out... break and throw exception } final long now = currentTime(); if (poolEntry.isMarkedEvicted() || (elapsedMillis(poolEntry.lastAccessed, now) > ALIVE_BYPASS_WINDOW_MS && !isConnectionAlive(poolEntry.connection))) { closeConnection(poolEntry, poolEntry.isMarkedEvicted() ? EVICTED_CONNECTION_MESSAGE : DEAD_CONNECTION_MESSAGE); timeout = hardTimeout - elapsedMillis(startTime); } else { metricsTracker.recordBorrowStats(poolEntry, startTime); return poolEntry.createProxyConnection(leakTaskFactory.schedule(poolEntry), now); } } while (timeout > 0L); metricsTracker.recordBorrowTimeoutStats(startTime); throw createTimeoutException(startTime); } catch (InterruptedException e) { Thread.currentThread().interrupt(); throw new SQLException(poolName + " - Interrupted during connection acquisition", e); } finally { suspendResumeLock.release(); } }
注意,這裏若是取到的鏈接是poolEntry.isMarkedEvicted(),則會close掉,同時更新timeout值,而後繼續循環borrow鏈接
HikariCP-2.7.6-sources.jar!/com/zaxxer/hikari/pool/HikariPool.javagithub
/** * Evict a Connection from the pool. * * @param connection the Connection to evict (actually a {@link ProxyConnection}) */ public void evictConnection(Connection connection) { ProxyConnection proxyConnection = (ProxyConnection) connection; proxyConnection.cancelLeakTask(); try { softEvictConnection(proxyConnection.getPoolEntry(), "(connection evicted by user)", !connection.isClosed() /* owner */); } catch (SQLException e) { // unreachable in HikariCP, but we're still forced to catch it } }
這種是主動調用evictConnection的場景
HikariCP-2.7.6-sources.jar!/com/zaxxer/hikari/pool/HikariPool.javasql
/** * Creating new poolEntry. If maxLifetime is configured, create a future End-of-life task with 2.5% variance from * the maxLifetime time to ensure there is no massive die-off of Connections in the pool. */ private PoolEntry createPoolEntry() { try { final PoolEntry poolEntry = newPoolEntry(); final long maxLifetime = config.getMaxLifetime(); if (maxLifetime > 0) { // variance up to 2.5% of the maxlifetime final long variance = maxLifetime > 10_000 ? ThreadLocalRandom.current().nextLong( maxLifetime / 40 ) : 0; final long lifetime = maxLifetime - variance; poolEntry.setFutureEol(houseKeepingExecutorService.schedule( () -> { if (softEvictConnection(poolEntry, "(connection has passed maxLifetime)", false /* not owner */)) { addBagItem(connectionBag.getWaitingThreadCount()); } }, lifetime, MILLISECONDS)); } return poolEntry; } catch (Exception e) { if (poolState == POOL_NORMAL) { // we check POOL_NORMAL to avoid a flood of messages if shutdown() is running concurrently LOGGER.debug("{} - Cannot acquire connection from data source", poolName, (e instanceof ConnectionSetupException ? e.getCause() : e)); } return null; } } /** * "Soft" evict a Connection (/PoolEntry) from the pool. If this method is being called by the user directly * through {@link com.zaxxer.hikari.HikariDataSource#evictConnection(Connection)} then {@code owner} is {@code true}. * * If the caller is the owner, or if the Connection is idle (i.e. can be "reserved" in the {@link ConcurrentBag}), * then we can close the connection immediately. Otherwise, we leave it "marked" for eviction so that it is evicted * the next time someone tries to acquire it from the pool. * * @param poolEntry the PoolEntry (/Connection) to "soft" evict from the pool * @param reason the reason that the connection is being evicted * @param owner true if the caller is the owner of the connection, false otherwise * @return true if the connection was evicted (closed), false if it was merely marked for eviction */ private boolean softEvictConnection(final PoolEntry poolEntry, final String reason, final boolean owner) { poolEntry.markEvicted(); if (owner || connectionBag.reserve(poolEntry)) { closeConnection(poolEntry, reason); return true; } return false; }
注意在createPoolEntry的時候註冊了一個延時任務,並經過poolEntry.setFutureEol設置到poolEntry中softEvictConnection,首先標記markEvicted。而後若是是用戶本身調用的,則直接關閉鏈接;若是從connectionBag中標記不可borrow成功,則關閉鏈接dom
這個定時任務是在每次createPoolEntry的時候,根據maxLifetime隨機設定一個variance,在maxLifetime - variance以後觸發evictui
hikari鏈接池的maxLifetime用來標記connection在鏈接池中的存活時間,爲0表示無限期。其到期的操做,主要是依靠在建立poolEntry的時候,註冊一個延時任務,在鏈接存活將要到達maxLifetime以前觸發evit,用來防止出現大面積的connection因maxLifetime同一時刻失效。除了這個延時任務,用戶也能夠主動去調用evict標記鏈接爲evict。this
觸發時間距離maxlifetime的差值是根據 maxLifetime > 10_000 ? ThreadLocalRandom.current().nextLong( maxLifetime / 40 ) : 0;
來計算(up to 2.5% of the maxlifetime
)。debug
標記爲evict只是表示鏈接池中的該鏈接不可用,但還在鏈接池當中,還會被borrow出來,只是getConnection的時候判斷了,若是是isMarkedEvicted,則會從鏈接池中移除該鏈接,而後close掉。code