druid spring boot下使用@ConfigurationProperties配置数据源后, 访问/actuator/configprops接口导致系统卡死

epfja78i  于 2022-12-31  发布在  Druid
关注(0)|答案(8)|浏览(247)

环境信息

  • 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 对象, 所以会导致死循环, 导致不停的调用 DruidDataSourcegetConnection 方法, 最终耗尽连接.

以上是我的一些猜测, 还有待验证. 同时对于为什么去除 @ConfigurationProperties 后的代码, 系统就可以正常运行, 我目前还不清楚, 还在看代码.

总结

我不清楚, 这个问题是 Druid 的, 还是 Spring Boot 的, 抑或是我的使用方式问题, 我只是把相关信息提供给作者, 希望能得到回复.
最后, 感谢Druid项目团队的无私贡献!!!

2wnc66cl

2wnc66cl1#

请问你解决了吗,我也遇到了

svujldwt

svujldwt2#

请问你解决了吗,我也遇到了

没有, 我把/actuator/configprops接口禁用了

2w3rbyxf

2w3rbyxf3#

请问你解决了吗,我也遇到了

没有, 我把/actuator/configprops接口禁用了

不要在DataSource上加@ConfigurationProperties(prefix="spring.datasource"),采用别的方式读取配置,可以用@EnableConfigurationPropertie 在IOC容器中注入你的properties

vfh0ocws

vfh0ocws4#

@Bean
 @ConfigurationProperties(prefix="spring.datasource")
 public DataSource druidDataSource() {
     return new DruidDataSource();
 }

改为
@Bean @ConfigurationProperties(prefix="spring.datasource") public DataSource druidDataSource() { return DruidDataSourceBuilder.create().build(); }

解决configpros endpoints 进入 死循环 的问题

dly7yett

dly7yett5#

用BeanDefinitionBuilder动态注册的datasource 也会碰到这个问题..

o8x7eapl

o8x7eapl6#

@Bean
 @ConfigurationProperties(prefix="spring.datasource")
 public DataSource druidDataSource() {
     return new DruidDataSource();
 }

改为
@Bean @ConfigurationProperties(prefix="spring.datasource") public DataSource druidDataSource() { return DruidDataSourceBuilder.create().build(); }

解决configpros endpoints 进入 死循环 的问题

亲测有效,感谢大神。

lndjwyie

lndjwyie7#

@Bean
 @ConfigurationProperties(prefix="spring.datasource")
 public DataSource druidDataSource() {
     return new DruidDataSource();
 }

改为 @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使用!!!

uqdfh47h

uqdfh47h8#

亲测druid升级到1.2.8版本这个问题已不复现,我是引入spring-boot-admin 后出现此问题

相关问题