手動創建DruidDataSource失敗後死循環以及程序卡住的問題

druid的版本:1.1.20

這個問題在druid的github源碼上一直是個open的issue,見34883357

druid的配置,注意timeBetweenEvictionRunsMillis參數,這是造成卡的原因。這個參默認值爲60 * 1000L(60秒),單位是毫秒

# Druid配置
spring.datasource.druid.initial-size=5
spring.datasource.druid.min-idle=5
spring.datasource.druid.maxActive=20
spring.datasource.druid.maxWait=60000
spring.datasource.druid.timeBetweenEvictionRunsMillis=60000
spring.datasource.druid.minEvictableIdleTimeMillis=300000
spring.datasource.druid.validationQuery=SELECT 1 FROM DUAL
spring.datasource.druid.testWhileIdle=true
spring.datasource.druid.testOnBorrow=false
spring.datasource.druid.testOnReturn=false
spring.datasource.druid.poolPreparedStatements=true
spring.datasource.druid.maxPoolPreparedStatementPerConnectionSize=20
spring.datasource.druid.filters=stat,wall,log4j
spring.datasource.druid.connectionProperties=druid.stat.mergeSql\=true;druid.stat.slowSqlMillis\=5000
spring.datasource.druid.web-stat-filter.enabled=true
spring.datasource.druid.web-stat-filter.url-pattern=/*
spring.datasource.druid.web-stat-filter.exclusions=*.js,*.gif,*.jpg,*.bmp,*.png,*.css,*.ico,/druid/*
spring.datasource.druid.stat-view-servlet.url-pattern=/druid/*
spring.datasource.druid.stat-view-servlet.allow=127.0.0.1,10.53.145.28
#spring.datasource.druid.stat-view-servlet.deny=10.53.145.28
spring.datasource.druid.stat-view-servlet.reset-enable=false
spring.datasource.druid.stat-view-servlet.login-username=admin
spring.datasource.druid.stat-view-servlet.login-password=123456
現象:最後在使用Druid配置多數據源,需要手動創建DruidDataSource,在創建DruidDataSource失敗後會不停的打印日誌,如下(看時間一直在變,後臺日誌一直在刷)

在這裏插入圖片描述

網上的解決方法:在配置數據源時加上setBreakAfterAcquireFailure(true);,如下
    @Bean
    @ConfigurationProperties("spring.datasource.master")
    public DataSource masterDataSource() {
    	DruidDataSource dataSource = DruidDataSourceBuilder.create().build();
    	dataSource.setBreakAfterAcquireFailure(true);
        return dataSource;
    }
原理:看報錯信息類DruidDataSource.java:2715,下面有一段源碼,在2721行處當錯誤次數大於connectionErrorRetryAttempts且timeBetweenConnectErrorMillis 大於0,connectionErrorRetryAttempts默認值爲1,timeBetweenConnectErrorMillis 默認是500,可以通過配置項spring.datasource.druid.timeBetweenEvictionRunsMillis配置,從源碼的2733行可以看到設置breakAfterAcquireFailure爲true則會break。日誌狂刷的問題就終止了,但是這次拿到的connection是空的。爲下一個問題埋了坑。

在這裏插入圖片描述

這個解決了日誌狂刷的問題,但是程序卡住了,沒響應沒返回
2019-12-23 18:59:55.806  INFO 19724 --- [nio-8087-exec-1] o.a.c.c.C.[.[localhost].[/rwdemo]        : Initializing Spring DispatcherServlet 'dispatcherServlet'
2019-12-23 18:59:55.806  INFO 19724 --- [nio-8087-exec-1] o.s.web.servlet.DispatcherServlet        : Initializing Servlet 'dispatcherServlet'
2019-12-23 18:59:55.815  INFO 19724 --- [nio-8087-exec-1] o.s.web.servlet.DispatcherServlet        : Completed initialization in 9 ms
切換到slave2
feeId = 1
2019-12-23 18:59:55.967  INFO 19724 --- [nio-8087-exec-1] com.alibaba.druid.pool.DruidDataSource   : {dataSource-1} inited
2019-12-23 18:59:56.321 ERROR 19724 --- [eate-1736720965] com.alibaba.druid.pool.DruidDataSource   : create connection SQLException, url: jdbc:mysql://10.53.56.7:3306/blog?useSSL=false&characterEncoding=utf8&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true, errorCode 1045, state 28000

java.sql.SQLException: Access denied for user 'root'@'10.53.56.1' (using password: YES)
	at com.mysql.cj.jdbc.exceptions.SQLError.createSQLException(SQLError.java:129) ~[mysql-connector-java-8.0.13.jar:8.0.13]
	at com.mysql.cj.jdbc.exceptions.SQLError.createSQLException(SQLError.java:97) ~[mysql-connector-java-8.0.13.jar:8.0.13]
	at com.mysql.cj.jdbc.exceptions.SQLExceptionsMapping.translateException(SQLExceptionsMapping.java:122) ~[mysql-connector-java-8.0.13.jar:8.0.13]
	at com.mysql.cj.jdbc.ConnectionImpl.createNewIO(ConnectionImpl.java:835) ~[mysql-connector-java-8.0.13.jar:8.0.13]
	at com.mysql.cj.jdbc.ConnectionImpl.<init>(ConnectionImpl.java:455) ~[mysql-connector-java-8.0.13.jar:8.0.13]
	at com.mysql.cj.jdbc.ConnectionImpl.getInstance(ConnectionImpl.java:240) ~[mysql-connector-java-8.0.13.jar:8.0.13]
	at com.mysql.cj.jdbc.NonRegisteringDriver.connect(NonRegisteringDriver.java:207) ~[mysql-connector-java-8.0.13.jar:8.0.13]
	at com.alibaba.druid.pool.DruidAbstractDataSource.createPhysicalConnection(DruidAbstractDataSource.java:1643) ~[druid-1.1.20.jar:1.1.20]
	at com.alibaba.druid.pool.DruidAbstractDataSource.createPhysicalConnection(DruidAbstractDataSource.java:1709) ~[druid-1.1.20.jar:1.1.20]
	at com.alibaba.druid.pool.DruidDataSource$CreateConnectionThread.run(DruidDataSource.java:2715) ~[druid-1.1.20.jar:1.1.20]

2019-12-23 18:59:56.329 ERROR 19724 --- [eate-1736720965] com.alibaba.druid.pool.DruidDataSource   : create connection SQLException, url: jdbc:mysql://10.53.56.7:3306/blog?useSSL=false&characterEncoding=utf8&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true, errorCode 1045, state 28000

java.sql.SQLException: Access denied for user 'root'@'10.53.56.1' (using password: YES)
	at com.mysql.cj.jdbc.exceptions.SQLError.createSQLException(SQLError.java:129) ~[mysql-connector-java-8.0.13.jar:8.0.13]
	at com.mysql.cj.jdbc.exceptions.SQLError.createSQLException(SQLError.java:97) ~[mysql-connector-java-8.0.13.jar:8.0.13]
	at com.mysql.cj.jdbc.exceptions.SQLExceptionsMapping.translateException(SQLExceptionsMapping.java:122) ~[mysql-connector-java-8.0.13.jar:8.0.13]
	at com.mysql.cj.jdbc.ConnectionImpl.createNewIO(ConnectionImpl.java:835) ~[mysql-connector-java-8.0.13.jar:8.0.13]
	at com.mysql.cj.jdbc.ConnectionImpl.<init>(ConnectionImpl.java:455) ~[mysql-connector-java-8.0.13.jar:8.0.13]
	at com.mysql.cj.jdbc.ConnectionImpl.getInstance(ConnectionImpl.java:240) ~[mysql-connector-java-8.0.13.jar:8.0.13]
	at com.mysql.cj.jdbc.NonRegisteringDriver.connect(NonRegisteringDriver.java:207) ~[mysql-connector-java-8.0.13.jar:8.0.13]
	at com.alibaba.druid.pool.DruidAbstractDataSource.createPhysicalConnection(DruidAbstractDataSource.java:1643) ~[druid-1.1.20.jar:1.1.20]
	at com.alibaba.druid.pool.DruidAbstractDataSource.createPhysicalConnection(DruidAbstractDataSource.java:1709) ~[druid-1.1.20.jar:1.1.20]
	at com.alibaba.druid.pool.DruidDataSource$CreateConnectionThread.run(DruidDataSource.java:2715) ~[druid-1.1.20.jar:1.1.20]

2019-12-23 18:59:56.330  INFO 19724 --- [eate-1736720965] c.a.druid.pool.DruidAbstractDataSource   : {dataSource-1} failContinuous is true
程序卡住了,沒返回也沒響應

在這裏插入圖片描述

使用JVM工具(jconsole或jvisualvm)查看,線程卡在以下地方等待

在這裏插入圖片描述

原因:看線程卡住的地方,在DruidDataSource.java:2786行,源碼如下

在這裏插入圖片描述

可以看到當timeBetweenEvictionRunsMillis>0時暫停timeBetweenEvictionRunsMillis,否則暫停1秒。timeBetweenEvictionRunsMillis的默認值是60 * 1000(60秒) 嘗試了以下解決方法都不行,加上以下配置
# 修改timeBetweenEvictionRunsMillis參數,減少時間
spring.datasource.druid.timeBetweenEvictionRunsMillis=6000

# DruidDataSource.java:2724行,如果設置了該配置爲true
spring.datasource.druid.fail-fast=true
spring.datasource.druid.testOnBorrow=true

結論

該問題無解,可以換其它連接池,比如springboot 2.X默認的HikariCP,DBCP或tomcat pool

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章