这是一个同事碰到的案例。
SpringBoot 应用卡死、无反应。
1、 导出线程栈,发现 Tomcat 处理线程都阻塞在获取连接上,从栈上看连接池使用的是 druid。
2、 对照 druid 源码,发现线程一直被阻塞是因为没有设置获取连接的超时时间。而从配置来看是有设置的。被阻塞的线程栈如下:
"http-nio-8006-exec-200" #7057 daemon prio=5 os_prio=0 tid=0x00007fc82c0a3800 nid=0x1b99 waiting on condition [0x00007fc7c9a57000] java.lang.Thread.State: WAITING (parking) at sun.misc.Unsafe.park(Native Method) - parking to wait for <0x00000000c2923bd8> (a java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject) at java.util.concurrent.locks.LockSupport.park(LockSupport.java:175) at java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.await(AbstractQueuedSynchronizer.java:2039) at com.alibaba.druid.pool.DruidDataSource.takeLast(DruidDataSource.java:1444) at com.alibaba.druid.pool.DruidDataSource.getConnectionInternal(DruidDataSource.java:1088) at com.alibaba.druid.pool.DruidDataSource.getConnectionDirect(DruidDataSource.java:953) at com.alibaba.druid.filter.FilterChainImpl.dataSource_connect(FilterChainImpl.java:4544) at com.alibaba.druid.filter.stat.StatFilter.dataSource_getConnection(StatFilter.java:661) at com.alibaba.druid.filter.FilterChainImpl.dataSource_connect(FilterChainImpl.java:4540) at com.alibaba.druid.pool.DruidDataSource.getConnection(DruidDataSource.java:931) at com.alibaba.druid.pool.DruidDataSource.getConnection(DruidDataSource.java:923) at com.alibaba.druid.pool.DruidDataSource.getConnection(DruidDataSource.java:100)
3、 通过内存 dump 发现,druid 连接池除 连接串、用户名、密码等几个属性之外的属性都是默认值。
此时连接池里总共有8个连接,都是空闲的,却没有线程能获取到连接,都在阻塞、没有被唤醒。网上查了下,应该是 druid 的bug。
4、 再查看 druid-spring-boot-starter 源码,发现上面几个属性的前缀是 spring.datasource
,其他属性的前缀是 spring.datasource.druid
。
druid-spring-boot-starter 关于配置的举例
spring.datasource.url= spring.datasource.username= spring.datasource.password= # Druid 数据源配置,继承spring.datasource.* 配置,相同则覆盖 spring.datasource.druid.initial-size=5 spring.datasource.druid.max-active=5
5、 这个应用的另一个问题是,他们对每个请求进行拦截校验签名,签名的秘钥信息是存在数据库里,处理过程为:1. 从数据库取秘钥信息,2. 进行加解密并校验。开发人员把整个过程放在一个只读事务里。其实他这个加解密是比较耗时的,由于有事务,数据库连接只能在事务结束时才释放。这就降低了连接使用效率。
本人负责的项目,由于使用了 Apollo,为了验证实时刷新配置,有检查每个配置项的值、且采用的 HikariCP ,未踩中这个问题。
欢迎关注我的微信公众号: coderbee笔记 ,可以更及时回复你的讨论。