最近做的一个需求部署测试环境后,运行起来后,发现部分接口出现响应超时情况,一个数据导入接口甚至出现卡顿情况。于是开启了问题排查之路。
1、定位问题
接口出现卡顿,首先想到的是看下线程堆栈信息,于是通过jstack命令打印当前线程堆栈。在jdos上提供了便捷的命令操作,如下图所示:
拿到线程堆栈信息后,根据接口日志上的ThreadId搜索,查看线程的状态。惊奇的发现很多线程都处于WAITING状态,下面提供了堆栈截图:
从这里就很容易发现了问题,大量的线程都在等待从连接池获取数据库连接。项目中使用了Druid连接池,那是什么造成连接耗尽的呢?慢sql导致线程长时间占有连接?连接池泄漏?连接池配置有问题?
2、分析问题
- 慢sql导致的连接长时间未释放?
- 这个疑问很容易去排查,在易维平台中可以查看慢sql情况。发现没有慢sql存在。解除怀疑。
- 连接池泄漏?
- 要分析是否存在连接池泄漏情况,我们需要查看当前连接池中活跃连接情况,还好druid官方提供了监控功能,但是生产一般不建议开启。二话不说,在本地环境开始配置监控功能。参考官网(https://github.com/alibaba/druid)很容易搭建。这里要注意jar包版本,项目中使用的druid版本较低,很多监控不支持。建议升到较高版本操作。由于是在代码发版后出现的问题,就重点看了下新增的代码是否存在连接泄漏情况。一顿操作之后,首先确认了问题是连接池泄漏:打开的连接数>关闭的连接数
- 确认哪里造成了连接池泄漏,可以点击“活跃连接堆栈查看”,可以看到久久未释放连接的堆栈。如图所示:
- 按照官网说明配置,连接超时后会被自动释放,并打印日志。进一步确认了问题代码。
removeAbandoned: true
removeAbandonedTimeout: 1800
logAbandoned: true
- 连接被动释放时的日志截取:
[ERROR] (Druid-ConnectionPool-Destroy-228035257) [com.alibaba.druid.pool.DruidDataSource:2915]: abandon connection, owner thread: http-nio-80-exec-2, connected at : 1663564101647, open stackTrace
at java.lang.Thread.getStackTrace(Thread.java:1564)
at com.alibaba.druid.pool.DruidDataSource.getConnectionDirect(DruidDataSource.java:1460)
at com.alibaba.druid.filter.FilterChainImpl.dataSource_connect(FilterChainImpl.java:5059)
at com.alibaba.druid.filter.FilterAdapter.dataSource_getConnection(FilterAdapter.java:2756)
at com.alibaba.druid.filter.FilterChainImpl.dataSource_connect(FilterChainImpl.java:5055)
at com.alibaba.druid.filter.stat.StatFilter.dataSource_getConnection(StatFilter.java:680)
at com.alibaba.druid.filter.FilterChainImpl.dataSource_connect(FilterChainImpl.java:5055)
at com.alibaba.druid.pool.DruidDataSource.getConnection(DruidDataSource.java:1373)
at com.alibaba.druid.pool.DruidDataSource.getConnection(DruidDataSource.java:1365)
- 这里犯了一个低级错误,在使用sqlSession操作之后,没有关闭。造成了连接池泄漏。
3、总结
在进行druid监控配置过程中,发现代码中的druid属性配置并未生效。由一个泄漏问题排查出另外一个项目配置的问题。druid的配置,官网提供了两种方式。一种是通过https://github.com/alibaba/druid/tree/master/druid-spring-boot-starter/,一种是通过spring集成。但是要特别注意,通过spring集成时,yml文件中配置的druid属性,需要我们手动加载。可参考(https://segmentfault.com/a/1190000039005979)
@ConfigurationProperties(prefix = "spring.datasource")
@Bean
public DataSource dataSource() {
return new DruidDataSource();
}