环境信息
- jdk1.8_191
- spring-boot-starter-*: 2.0.7.RELEASE
- druid-spring-boot-starter: 1.1.10
- mysql-connector-java: 5.1.47
- mysql数据库: 5.7.21
问题描述
我通过 druid-spring-boot-starter:1.1.10
引入druid后, 通过 @ConfigurationProperties(prefix="spring.datasource")
自定义创建了datasource对象, 应用启动后, 访问项目的 /actuator/configprops
接口(这是spring boot提供的一个 监控接口 ), 请求一直没有响应. 后来通过dump分析, 发现数据库连接耗尽, 所有请求会一直卡在 com.alibaba.druid.pool.DruidDataSource#takeLast
方法的1899行, 该行内容为 notEmpty.await();
. 完整的项目内容如下:
- pom.xml
<?xmlversion="1.0"encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.demo</groupId>
<artifactId>druid</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>druid</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<version>2.0.7.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
<version>2.0.7.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-autoconfigure</artifactId>
<version>2.0.7.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
<version>2.0.7.RELEASE</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
<version>1.1.10</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.47</version>
<scope>runtime</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<version>2.0.7.RELEASE</version>
</plugin>
</plugins>
</build>
</project>
- spring配置文件: application.properties
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
spring.datasource.url=jdbc:mysql://127.0.0.1:3306/test
spring.datasource.username=root
spring.datasource.password=root
management.endpoints.web.exposure.include=*
management.endpoint.health.show-details=always
- 启动类: DruidApplication
import javax.sql.DataSource;
import com.alibaba.druid.pool.DruidDataSource;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
@SpringBootApplication
public class DruidApplication {
public static void main(String[] args) {
SpringApplication.run(DruidApplication.class, args);
}
@Bean
@ConfigurationProperties(prefix="spring.datasource")
public DataSource druidDataSource() {
return new DruidDataSource();
}
}
应用启动后访问 http://localhost:8080/actuator/configprops
, 请求会一直卡着. 之前也有一个关于这个问题的issue: #2789 , 但是这个issue没有任何讨论就被关闭了. 偶然情况下, 我把启动类: DruidApplication内的这段代码删除
@Bean
@ConfigurationProperties(prefix="spring.datasource")
public DataSource druidDataSource() {
return new DruidDataSource();
}
应用就可以正常运行了, 请求 /actuator/configprops
也有正常的结果返回. 但是这个问题并不能通过删除这段配置完全解决, 比如在一些历史项目里, 数据源配置信息不是标准的 spring.datasource.xxx
格式, 还是需要增加如上配置的, 此时问题就又会出现了.
一些分析内容
下面是我在应用卡时对dump下来的文件进行分析后得到的一些信息:
首先是处理 /actuator/configprops
请求的线程的线程栈, 线程id: 0x5cea7ffb0, 这个线程卡在了 com.alibaba.druid.pool.DruidDataSource#takeLast
方法上, 等待获取数据库连接
通过查看数据库连接对象 com.mysql.jdbc.JDBC4Connection
的引用关系发现, 所有的数据库连接都被 0x5cea7ffb0这个线程持有了
也就说, 线程0x5cea7ffb0拿走了所有连接, 却还想获取新的连接, 所以它自己卡住了, 同时其他请求的线程也会由于无可用连接而卡住.
一点猜测
后来我通过阅读druid相关源码和调试, 有了一些猜测.
首先 /actuator/configprops
接口的处理类会调用 org.springframework.boot.actuate.context.properties.ConfigurationPropertiesReportEndpoint#safeSerialize
方法获取应用中配置的属性信息, 这个方法的处理方式是使用Jackson对属性信息进行JSON序列化.
在序列化 DruidDataSource
时, 会调用其 getConnection
方法, 返回 DruidPooledConnection
对象. 接着对 DruidPooledConnection
序列化时又会调用其 getConnectionHolder
方法, 返回 DruidConnectionHolder
对象, 而这个对象内又持有一开始的 DruidDataSource
对象, 所以会导致死循环, 导致不停的调用 DruidDataSource
的 getConnection
方法, 最终耗尽连接.
以上是我的一些猜测, 还有待验证. 同时对于为什么去除 @ConfigurationProperties
后的代码, 系统就可以正常运行, 我目前还不清楚, 还在看代码.
总结
我不清楚, 这个问题是 Druid
的, 还是 Spring Boot
的, 抑或是我的使用方式问题, 我只是把相关信息提供给作者, 希望能得到回复.
最后, 感谢Druid项目团队的无私贡献!!!
8条答案
按热度按时间2wnc66cl1#
请问你解决了吗,我也遇到了
svujldwt2#
请问你解决了吗,我也遇到了
没有, 我把/actuator/configprops接口禁用了
2w3rbyxf3#
请问你解决了吗,我也遇到了
没有, 我把/actuator/configprops接口禁用了
不要在DataSource上加@ConfigurationProperties(prefix="spring.datasource"),采用别的方式读取配置,可以用@EnableConfigurationPropertie 在IOC容器中注入你的properties
vfh0ocws4#
改为
@Bean @ConfigurationProperties(prefix="spring.datasource") public DataSource druidDataSource() { return DruidDataSourceBuilder.create().build(); }
解决configpros endpoints 进入 死循环 的问题
dly7yett5#
用BeanDefinitionBuilder动态注册的datasource 也会碰到这个问题..
o8x7eapl6#
改为
@Bean @ConfigurationProperties(prefix="spring.datasource") public DataSource druidDataSource() { return DruidDataSourceBuilder.create().build(); }
解决configpros endpoints 进入 死循环 的问题
亲测有效,感谢大神。
lndjwyie7#
改为
@Bean @ConfigurationProperties(prefix="spring.datasource") public DataSource druidDataSource() { return DruidDataSourceBuilder.create().build(); }
解决configpros endpoints 进入 死循环 的问题
我遇到的问题是目前框架把
com.alibaba.druid.spring.boot.autoconfigure.DruidDataSourceWrapper
既当Datasource Bean,又当 properties Bean,通过/configprops
端口获取配置信息时,contexts.{name}.beans.dataSource.inputs.sqlStatMap
节点会把所有监控数据当成属性返回给前端,随着时间推移,该数据可能达到几十甚至上百M,导致系统卡死。请框架不要把Datasource Bean直接当properties Bean使用!!!
uqdfh47h8#
亲测druid升级到1.2.8版本这个问题已不复现,我是引入spring-boot-admin 后出现此问题