问题现象
最近在spring boot项目中引入了 spring-boot-starter-actuator 后,测试环境开始出现服务假死的现象,
且这个问题十分怪异,只在多个微服务中的简称A的这个服务中出现,其他服务都没有出现这个问题,
之所以说他是假死,是因为只是http请求无法访问进去了,但是该服务的定时任务却可以定时执行。
问题排查
通过查看jvm的线程信息发现,假死的A服务中,存在很多目前正在waiting状态的http nio线程,
进一步跟踪这些线程的堆栈信息,发现他们都在Alibaba Druid连接池的获取Connection方法中等待获取到最新的Connection,
第一反应是不是连接泄露了,存在慢sql、阻塞住的sql,或者手动获取connection但是没有归还的现象,
于是通过在定时任务代码中打印连接池的状态,发现也没有这种情况,而且连接池的最大连接数设置的也挺大的。
初步定位
通过观察该连接池的链接对象,发现是一个sql server数据库,但是我们的项目配置连接的其实是mysql,
在代码中搜索发现了有一个手动创建连接池的地方,而这里是为了与一个客户的sql server数据库做同步使用的,但是在给其他的客户部署时,并不需要这个操作,代码如下:
这里的操作有一个最大的问题,就是当不设置Druid连接池的等待连接时间时,该时间是-1,即默认永远等待,永不超时
最终定位
有了上面的代码,则需要确认为什么获取不到connection?造成永远等待?
这很正常,因为其他的客户环境并没有sql server,完全无法连接上啊,这时候Druid连接池中的可用connection压根没有,
然后spring boot admin,又不停的通过http轮询检查服务健康状态,最终它的每次http请求都会陷入阻塞等待connection,加之http又没有设置连接超时时间,
最终健康检查http连接占满了服务的http连接,导致其他的请求无法进入。
解决与避免
这里存在几个问题
- 该代码应当在指定客户环境运行,其余客户不应当创建该连接池
- 连接池配置应当通过yml文件配置,这样可以尽快的发现系统中存在的相关io组件,并发现其配置的不正确性,spring boot支持多数据源配置。