digdeep

凡是过去,皆是序幕。Read the fucking manual and source code.

  博客园 :: 首页 :: 博问 :: 闪存 :: 新随笔 :: 联系 :: 订阅 订阅 :: 管理 ::

线下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,但是还是会占用一个数据库连接的原因所在。

 

 

 

  

posted on 2020-12-10 12:54  digdeep  阅读(14903)  评论(0编辑  收藏  举报
不懂数据库和Web安全的架构师不是一个好的程序员。