本文主要研究一下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