序
本文主要研究下hikari连接池的validationTimeout
validationTimeout
默认是5秒 HikariCP/2.7.6/HikariCP-2.7.6-sources.jar!/com/zaxxer/hikari/pool/PoolBase.java
boolean isConnectionAlive(final Connection connection) { try { try { setNetworkTimeout(connection, validationTimeout); final int validationSeconds = (int) Math.max(1000L, validationTimeout) / 1000; if (isUseJdbc4Validation) { return connection.isValid(validationSeconds); } try (Statement statement = connection.createStatement()) { if (isNetworkTimeoutSupported != TRUE) { setQueryTimeout(statement, validationSeconds); } statement.execute(config.getConnectionTestQuery()); } } finally { setNetworkTimeout(connection, networkTimeout); if (isIsolateInternalQueries && !isAutoCommit) { connection.rollback(); } } return true; } catch (Exception e) { lastConnectionFailure.set(e); LOGGER.warn("{} - Failed to validate connection {} ({})", poolName, connection, e.getMessage()); return false; } } /** * Set the network timeout, ifisUseNetworkTimeout
istrue
and the * driver supports it. * * @param connection the connection to set the network timeout on * @param timeoutMs the number of milliseconds before timeout * @throws SQLException throw if the connection.setNetworkTimeout() call throws */ private void setNetworkTimeout(final Connection connection, final long timeoutMs) throws SQLException { if (isNetworkTimeoutSupported == TRUE) { connection.setNetworkTimeout(netTimeoutExecutor, (int) timeoutMs); } } /** * Set the query timeout, if it is supported by the driver. * * @param statement a statement to set the query timeout on * @param timeoutSec the number of seconds before timeout */ private void setQueryTimeout(final Statement statement, final int timeoutSec) { if (isQueryTimeoutSupported != FALSE) { try { statement.setQueryTimeout(timeoutSec); isQueryTimeoutSupported = TRUE; } catch (Throwable e) { if (isQueryTimeoutSupported == UNINITIALIZED) { isQueryTimeoutSupported = FALSE; LOGGER.info("{} - Failed to set query timeout for statement. ({})", poolName, e.getMessage()); } } } }
如果是jdbc4的话,可以使用isUseJdbc4Validation,是直接利用connection.isValid(validationSeconds)来验证连接的有效性;否则的话则用connectionTestQuery查询语句来查询验证。
this.isUseJdbc4Validation = config.getConnectionTestQuery() == null;当connectionTestQuery为null的时候,isUseJdbc4Validation为true
HikariPool.getConnection
HikariCP-2.7.6-sources.jar!/com/zaxxer/hikari/pool/HikariPool.java
/** * 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(); } }
可以看到borrow到poolEntry之后,如果不是isMarkedEvicted,则会调用isConnectionAlive来判断连接的有效性
HikariPool
HikariCP-2.7.6-sources.jar!/com/zaxxer/hikari/pool/HikariPool.java
/** * The house keeping task to retire and maintain minimum idle connections. */ 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 ListnotInUse = 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); } } }
HouseKeeper线程也用到了validationTimeout
小结
validationTimeout用来指定验证连接有效性的超时时间(默认是5秒,最小不能小于250毫秒
),如果是没有设置connectionTestQuery的话,默认是用jdbc4规范中的connection.isValid(validationSeconds)来验证连接的有效性。
另外hikari是在borrow连接的时候校验连接的有效性,相当于tomcat jdbc pool的testOnBorrow=true