上周几次接到同事电话说线上环境用户登陆失败,我一查线上日志看到很多Connection timeout的错,
因为我们用到了HikariCP连接池,理论上来说不会出现连接泄漏的问题,我就加了HikariCP的leakDetectionThreshold
连接泄漏检测配置。
今晚又被提示说登陆不上了,我一看日志,果真如提前预料的那样也是Connection timeout
的错,但是这次竟然日志中没有出现Apparent connection leak detected
类似的字样,甚是奇怪。
其实在去年年底的时候已经出现过几次,当时我是定位到mybatisplus中ActiveRecord用法(比如updateById等操作)会导致Connection timeout,当时也没去深入源码看是什么问题就草草略过,只是叫同事不要用ActiveRecord的操作(可惜同事还是继续用的乐此不疲),后来又遇到多数据源下Spring事务不生效的问题,是因为不是用同一个dynamicDatasource的Bean导致的,之后也想当然认为ActiveRecord引发的Connection timout是这个问题引起的,当时系统也没多少用户访问,所以问题没暴露的频繁。
这次几回出现这个异常看来是另有隐情, 我想从根本上解决问题,首先就把日志级别调为debug模式,然后我结合着用户日志行为分析(这几次是每到傍晚6点过后就会出现问题),模拟用户界面操作,果然过一会连接数飙升,在HikariCP经过Cleanup后也没移除,既有连接一直处于active状态,说明连接是存在泄漏。接着我发现了为什么leakDetectionThreshold
没生效?竟然是因为我没在代码里加上相应配置,只是在配置文件中加上而已(因为我们是用的自定义数据源,所以配置也需要在HikariCP的配置Bean中手动加上)。
果然过了我配置的60s检测时间过后就抛出了一条Apparent connection leak detected
的异常,异常定位到我们使用ActiveRecord的updateById那边,这边我也同时想到了 在我们写的类似代码里调用ActiveRecord的时候是不会被Spring管理的,因为我们操作的Entity是自己new出来然后去selectById的,不是经过注入的Service去调用的。 接着我下意识点开updateById的定义,发现了重要线索,里面竟然在update过后没有调用closeSession,我也看了insert方法是有close操作的。所以到这里我想应该是mybatisplus本身的bug,然后我一看我们项目引用的版本竟然是RC版,我瞬间感觉崩溃(当时竟然没去考虑这个版本问题)。为了证明这一点,我去github上的mybatisplus版本release记录中去看,发现他们在3.0.4中才把这个bug修复,加上了finally close的操作,而我们用的只是3.0.1-RC版本。
最后我把pom中mybatisplus版本升到了3.0.4把这个坑给填上了。
参考资料: