Timeout: Pool empty. Unable to fetch a connection in 30 seconds, none available[size:200; busy:200; idle:0; lastwait:30000]

这个问题之前在项目中就困扰了我很多次,只不过重新部署就好了而且再也没有出现过了,所以也就没注意。但是今天这个问题一直出现,重新部署运行一会儿就出现,运行一会就出现。不知道怎么弄,没办法就硬着头皮上网搜,花了一天时间,也算是把这奇葩的BUG解决了,这里记录一下解决的几个过程。
首先讲一下场景,项目是一个物联网项目,会有硬件向服务器上报信息,硬件数量多,上传频率高,上来的数据需要做处理,在这种高并发的环境下,我先是尽可能不去数据库取数据,但是无可避免的还是会需要访问到数据库,退无可退了,只能开始找原因了。
故障的意思很简单直接,就是说数据库的连接池空了,在超时时限内没有获取到连接,则抛出异常,于是去数据库查看连接

Mysql -> show full processlist;

通过这句话在MySQL中去获取所有进程,看看自己这个项目的进程,确实有很多,而且在Command这一栏还都是sleep,想来可能跟这个有关系,于是乎上网搜相关内容,搜来的大致意思是连接没有正确关闭。不过我代码里面用到的SQL是关闭了的,虽然体感是这个问题,但是从这入手应该是解决不了问题。
但是关于这个sleep的问题,又引申到了MySQL的wait_timeout和interactive_timeout的这两个属性,这两个都是空闲连接的超时时间,只不过前者是非交互式连接的(通过jdbc连接),后者是交互式连接的(通过MySQL客户端连接)。于是尝试设置一下wait_timeout的时间,这里默认设置的是8h

Mysql -> set global wait_timeout=30;

设置成30s之后,重新部署,跑起来之后确实是有效的,那些sleep的进程都消失了,感觉好像这样就OK了,但是接下来又报了另外一个异常,具体异常忘记记录了,大致意思是连接丢失,上网搜一下发现时wait_timeout时间设置太短造成的。所以感觉修改wait_timeout的时间不是正确的解,于是又重新设置成了8H,再从别的地方入手。
这条异常记录上有提到size是100,于是乎想到提高最大连接量试试(其实这是个很笨的主意,因为100能超,1000估计都一样超,时间问题而已)。这就又涉及到了Tomcat-jdbc连接池的配置,但是之前我试着去配置连接池,想让最大连接量能大一些,只不过没有用,先贴一下原先的配置,由于是spring-boot项目,所以配置是写在properties里面

#连接池最大连接数
spring.datasource.tomcat.max-active=200
#空闲池中最大连接数
spring.datasource.tomcat.max-idle=50
#空闲池中最小连接数
spring.datasource.tomcat.min-idle=10
spring.datasource.tomcat.initial-size=10

本意是想让连接池的连接数变成200的,但是不起作用,stackoverflow上看是说spring-boot的1.4.1版本有一个更改,本来是spring.datasource.max-active=200的,现在得要根据具体情况来调整,比如用Tomact就是spring.datasource.tomcat.max-active=200。只不过我是1.4.3版本,我以为是向上兼容,没想到居然是向下兼容,改为不带Tomcat的就可以了,加上又看了一下连接池的配置,最终连接池的配置版本为

#连接池最大连接数
spring.datasource.max-active=200
#空闲池中最大连接数
spring.datasource.max-idle=50
#空闲池中最小连接数
spring.datasource.min-idle=10
spring.datasource.initial-size=10
#连接在池中空闲最小时间后被清除
spring.datasource.min-evictable-idle-time-millis=60000
#隔多久时间清回收废弃连接
spring.datasource.time-between-eviction-runs-millis=30000
#每次调用检测池里连接的可用性,假如连接池中的连接被数据库关闭了,应用通过连接池getConnection时会重新创建
spring.datasource.testOnBorrow=true
spring.datasource.validation-query=SELECT 1
#移除被遗弃的连接
spring.datasource.remove-abandoned=true
#设置超时时间
spring.datasource.tomcat.remove-abandoned-timeout=60

不得不说,在高并发的应用环境下,连接池的配置是真的重要,关于连接池的配置我感觉可以另开一篇来介绍了。最下面两个就是确实解决掉这个BUG所需要的配置,注释也稍微写了一下,是移除被遗弃的连接,超过60秒就被判断为遗弃的连接。这里的遗弃的连接就是在代码过程中写的比如没有及时关闭的连接之类的糟糕的写法。其实针对这个问题,体感上来说也确实是这么回事,连接用完之后,超过一个设定的时间就自动删掉。这点有点像Java线程池框架中的Executors.newCachedThreadPool(),设定的是线程存活60秒就自动删掉,之前试过在60秒之内疯狂增加线程数,并将线程数设定的比较小,一段时间后就报错了,跟今天连接池的这种情况确实是很像。
大概就是这么个情况,仅仅提供一个结题思路,当中还有不少绕路,也怪自己对连接池的配置还并不了解,如果一开始就修改对连接池的配置的话,也不至于被这个BUG纠缠一整天了。

长路漫漫。。

你可能感兴趣的:(Timeout: Pool empty. Unable to fetch a connection in 30 seconds, none available[size:200; busy:200; idle:0; lastwait:30000])