一、背景
项目发生告警,但是并没有影响业务,看了下日志,红框里面有循环调用了3次 ,一直以为是外部的重试在重试,但是外部确没有重试记录,就深扒了代码
二、想法
我知道hikaricp获取连接之后会校验连接的有效性,就猜想如果校验失败了,那本次请求是不是就是失败了,如果这样的话,那就没校验的意义了,所以肯定有重试机制用以保证请求的完整性。
三、实践
HikariDataSource.class
@Overridepublic Connection getConnection() throws SQLException{if (isClosed()) {throw new SQLException("HikariDataSource " + this + " has been closed.");}if (fastPathPool != null) {return fastPathPool.getConnection();}// See http://en.wikipedia.org/wiki/Double-checked_locking#Usage_in_JavaHikariPool result = pool;if (result == null) {synchronized (this) {result = pool;if (result == null) {validate();LOGGER.info("{} - Starting...", getPoolName());try {pool = result = new HikariPool(this);this.seal();}catch (PoolInitializationException pie) {if (pie.getCause() instanceof SQLException) {throw (SQLException) pie.getCause();}else {throw pie;}}LOGGER.info("{} - Start completed.", getPoolName());}}}return result.getConnection();}
这里面调用了 result.getConnection(); result为hikariPool
HikariPool.class
public Connection getConnection() throws SQLException
{return getConnection(connectionTimeout);
}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) > aliveBypassWindowMs && !isConnectionAlive(poolEntry.connection))) {closeConnection(poolEntry, poolEntry.isMarkedEvicted() ? EVICTED_CONNECTION_MESSAGE : DEAD_CONNECTION_MESSAGE);// elapsedMillis 方法是将当前时间戳-startTime 得出的时间差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();}}
方法整体流程
- 获取锁:确保线程安全。
- 记录开始时间:用于后续计算超时和统计。
- 尝试获取连接:
- 在给定的超时时间内循环尝试获取连接。
elapsedMillis 方法是将当前时间戳-startTime 得出的时间差
- 如连接无效则关闭连接并继续尝试。
- 如连接有效则记录统计信息并返回连接。
- 在给定的超时时间内循环尝试获取连接。
- 超时处理:如在规定时间内未能成功获取连接,记录超时统计信息并抛出
SQLException
。 - 异常处理:捕获
InterruptedException
并抛出相应的SQLException
。 - 释放锁:确保锁被释放,即使在异常情况下