线下Tomcat出现故障,查看日志报错:
HikariPool-1 - Connection is not available, request timed out after 30000ms最后调查的问题是:
某个Service中有个方法使用 httpclient 调用了一个外部接口,而当时这个外部接口服务出现故障,导致httpclient调用卡死了,一直没有响应,因为在Service方法中,也就是在数据库事务中,httpclient调用卡死导致 数据库连接一直无法释放,卡死的httpclient调用不断的增大,数据库连接不断的被占用并且无法释放,最终达到了最大值:
<!-- 连接池中允许的最大连接数。缺省值:10;推荐的公式:((core_count * 2) + effective_spindle_count) -->
<property name="maximumPoolSize" value="15" />
其他功能需要访问数据库时,就报错:HikariPool-1 - Connection is not available, request timed out after 30000ms
这里问题的关键在于 不能在事务中调用外部接口,不能在事务中有耗时的操作等存在。这样会导致事务变成大事务,长时间占用数据库连接、有可能还导致事务长时间持有相关锁从而导致死锁。
最佳的解决办法,自然是把 httpclient 外部调用移到事务外面。实时证明,移到事务外面就恢复正常了。
另外,httpclient 调用应该加上超时现在,比如超过2秒没有返回,就断开:
URL url = new URL(urlAddress); URLConnection conn = url.openConnection(); conn.setConnectTimeout(2000); // 2秒没有连上,则超时 conn.setReadTimeout(2000); // 2秒没有响应,则超时
这样也可以缓解问题。但是最好的方法还是把外部调用移到事务外面!
这里有个原理需要弄清楚:
service方法里面就是一个 httpclient 调用,没有查询数据库,为什么还是 会占用数据库连接呢????
原因是 数据库事务肯定是需要基于数据库连接的!就行我们在mysql客户端里面执行:begin transaction 命令一样,先得连上mysql 。没有连上mysql,无法开启事务。
这也是为什么在 service方法里面没有查询mysql,但是还是会占用一个数据库连接的原因所在。