==================================================================
今天项目上频繁出现了大量的这样的报错
Caused by: java.sql.SQLTransientConnectionException: HikariPool-1 - Connection is not available, request timed out after 6000ms.
at com.zaxxer.hikari.pool.HikariPool.createTimeoutException(HikariPool.java:695)
at com.zaxxer.hikari.pool.HikariPool.getConnection(HikariPool.java:197)
at com.zaxxer.hikari.pool.HikariPool.getConnection(HikariPool.java:162)
at com.zaxxer.hikari.HikariDataSource.getConnection(HikariDataSource.java:128)
at org.springframework.jdbc.datasource.DataSourceUtils.fetchConnection(DataSourceUtils.java:158)
at org.springframework.jdbc.datasource.DataSourceUtils.doGetConnection(DataSourceUtils.java:116)
at org.springframework.jdbc.datasource.DataSourceUtils.getConnection(DataSourceUtils.ja
必看视频!获取2024年最新Java开发全套学习资料 备注Java
va:79)
… 75 more
百度了一下,很多都说需要把spring.datasource.hikari.connection-timeout
的值调大就行。但是我们试了一下之后发现效果并不明显,然后我们调大了spring.datasource.hikari.maximum-pool-size
的大小,这下有效果了。这是为什么呢?
==================================================================
一直以来,我都以为这个报错是hikari连接数据库时,创建数据库连接超时抛出的异常,但是翻了一下抛出异常的源码之后发现并不是这样的,简单的贴一下代码:
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);
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();
}
}
里面的代码虽然没有细究,但是从意思上看,应该是如果在hardTimeout时间内没有从连接池中获取到有效的连接,那就抛出异常,而抛出的正是日志中大量打印的那个异常。
==================================================================
从上面的源码中看,增加spring.datasource.hikari.connection-timeout
的值应该确实是有效的,最起码可以降低异常率,但是总连接数依然不足,所以看起来的现象应该是日志中的报错少了,但是系统会卡的要命。增加spring.datasource.hikari.maximum-pool-size
之后整个系统可用的最大连接数变多了,自然就不卡不报错了。
==========================================================================
为了验证我的想法,我写了一个demo
数据库相关配置文件:
#5个最小空闲连接,我把它理解为coreSize
spring.datasource.hikari.minimum-idle=5
#最大生命周期60s,测试用
spring.datasource.hikari.max-lifetime=60000
#一个连接空闲10s后,将会被回收
spring.datasource.hikari.idle-timeout=10
#连接池中的最大连接数
spring.datasource.hikari.maximum-pool-size=10
#获取连接的等待时长5s
spring.datasource.hikari.connection-timeout=5000
#判断连接是否有效
spring.datasource.hikari.connection-test-query=select 1
demo源码:
@SpringBootApplication(scanBasePackages = “com.example.juc.hikari”)
public class Main {
private static final Logger logger = LoggerFactory.getLogger(Main.class);
public static void main(String[] args) {
ConfigurableApplicationContext applicationContext = SpringApplication.run(Main.class, args);
DataSource dataSource = applicationContext.getBean(DataSource.class);
logger.info(“{}”, dataSource);
//先获取10个连接,就获取,不释放
int maxConnectionCount = 10;
List connections = new ArrayList<>();
for (int i = 0; i < maxConnectionCount; i++) {
try {
Connection connection = dataSource.getConnection();
connections.add(connection);
} catch (SQLException e) {
e.printStackTrace();
}
}
//起一个新的
Thread thread = new Thread(() -> {
Connection connection = null;
do {
try {
//开始尝试获取连接
connection = dataSource.getConnection();
} catch (SQLException e) {
logger.error(“连接获取失败:[{}]”, e.getMessage());
}
//如果获取失败,就再来一次
} while (connection == null);
logger.info(“获取到新的连接了!”);
try {
connection.close();
} catch (SQLException e) {
e.printStackTrace();
}
logger.info(“链接归还了!”);
}, “test”);
thread.start();
try {
logger.info(“主线程休眠10s”);
TimeUnit.SECONDS.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
//主线程休眠10秒之后,开始归还连接
connections.forEach(connection -> {
try {
connection.close();
logger.info(“归还了”);
} catch (SQLException e) {
e.printStackTrace();
}
});
最后
码字不易,觉得有帮助的可以帮忙点个赞,让更多有需要的人看到
又是一年求职季,在这里,我为各位准备了一套Java程序员精选高频面试笔试真题,来帮助大家攻下BAT的offer,题目范围从初级的Java基础到高级的分布式架构等等一系列的面试题和答案,用于给大家作为参考
以下是部分内容截图
connection.close();
logger.info(“归还了”);
} catch (SQLException e) {
e.printStackTrace();
}
});
最后
码字不易,觉得有帮助的可以帮忙点个赞,让更多有需要的人看到
又是一年求职季,在这里,我为各位准备了一套Java程序员精选高频面试笔试真题,来帮助大家攻下BAT的offer,题目范围从初级的Java基础到高级的分布式架构等等一系列的面试题和答案,用于给大家作为参考
以下是部分内容截图
[外链图片转存中…(img-Abg2PwQU-1716442469564)]