最新某系统经常出现无法登录,不能响应请求的情况。一开始找上我的时候,他们已经通过重启恢复了,然后让我查原因。我表示一脸懵逼,啥都没有就让我解决了,最起码得让我有东西分析吧。还好这个问题经常发生,于是就在一次故障发生的时候。

我使用jps查到进程pid,然后用jstack将堆栈信息保存到一个文件里,然后就用上了我不知道啥时候收藏的网站fastThread,将文件上传上去。

这是一张图片

嚯,这么多线程再WAITING状态。点进去一个看看。

这是一张图片

大多数线程的堆栈信息都跟上面差不多,都是在等从Druid数据库连接池等连接。

所以,原因就是数据库连接不够用了。然后我看了一眼数据库连接池的配置。

1
2
spring.datasource.maxActive=80
spring.datasource.removeAbandoned=false

以我对这个系统的了解,这个配置完全够了。

于是,我打开了druid的监控。

1
2
spring.datasource.druid.web-stat-filter.enabled=true
spring.datasource.druid.stat-view-servlet.enabled=true

监控页面的部分功能需要设置这个。

1
spring.datasource.druid.removeAbandoned=true

果然,removeAbandoned设置为true之后就有地方出现问题了,抛出了connection holder is null的异常错误。

这说明,有地方长时间占用数据库连接了,由于超时回收,导致事务提交的时候出现问题。找到出错的代码后发现是因为定时任务的所有操作都在一个事务里,一个定时任务几十分钟。其实,这个定时任务完全没必要加一个这样的大粒度事务,它的大多数操作都不必与数据库交互。所以,只要把事务粒度控制好了问题就解决了。

另外,还有一个原因是下面这个配置并不会生效。我其实挺不理解的,Spring Boot的文档写得很清楚了,为什么还有人把这些配置弄错了,而且还不验证有没有生效。

1
2
spring.datasource.maxActive=80
spring.datasource.removeAbandoned=false

因为系统用的Druid,所以正确的配置是下面这样的

1
2
spring.datasource.druid.maxActive=80
spring.datasource.druid.removeAbandoned=false