Druid是一个阿里巴巴开源的数据库连接池,功能比较强大,使用也非常广泛。
前段时间笔者在使用的过程中遇到了一个小Bug,这个Bug有50%的几率导致服务启动失败,该Bug只有在同时使用maxEvictableIdleTimeMillis,minEvictableIdleTimeMillis时会出现。
在说明这个bug之前,我们先来了解下maxEvictableIdleTimeMillis,minEvictableIdleTimeMillis这两个参数。
minEvictableIdleTimeMillis:最小空闲时间,默认30分钟,如果连接池中非运行中的连接数大于minIdle,并且那部分连接的非运行时间大于minEvictableIdleTimeMillis,则连接池会将那部分连接设置成Idle状态并关闭;也就是说如果一条连接30分钟都没有使用到,并且这种连接的数量超过了minIdle,则这些连接就会被关闭了。
maxEvictableIdleTimeMillis:最大空闲时间,默认7小时,如果minIdle设置得比较大,连接池中的空闲连接数一直没有超过minIdle,这时那些空闲连接是不是一直不用关闭?当然不是,如果连接太久没用,数据库也会把它关闭,这时如果连接池不把这条连接关闭,系统就会拿到一条已经被数据库关闭的连接。为了避免这种情况,Druid会判断池中的连接如果非运行时间大于maxEvictableIdleTimeMillis,也会强行把它关闭,而不用判断空闲连接数是否小于minIdle;
这两参数是在DestroyTask的shrink方法中用来判断连接是不是应该被关闭的。
// 关闭条件,空闲时间大于minEvictableIdleTimeMillis,并且空闲连接大于minIdle,// 其中checkCount为poolingCount - minIdle,即可能被关闭的连接数量// 或者空闲时间大于maxEvictableIdleTimeMillisif (idleMillis >= minEvictableIdleTimeMillis) {if (checkTime && i < checkCount) {evictConnections[evictCount++] = connection;continue;} else if (idleMillis > maxEvictableIdleTimeMillis) {evictConnections[evictCount++] = connection;continue;}}
BUG还原:
当时我在项目中设置了maxEvictableIdleTimeMillis=400000,minEvictableIdleTimeMillis=300000,使用@ConfigurationProperties 将属性注入DataSource。这样配置理论上应该是没问题的,但启动服务时有50%几率会抛出这个异常:maxEvictableIdleTimeMillis must be grater than minEvictableIdleTimeMillis。
master.druid.datasource.maxEvictableIdleTimeMillis=400000master.druid.datasource.minEvictableIdleTimeMillis=300000
@ConfigurationProperties(prefix = "master.druid.datasource")public DataSource masterDataSource() {return DataSourceBuilder.create().type(DruidDataSource.class).build();}
问题原因:
当设置maxEvictableIdleTimeMillis时,会判断该值是否大于minEvictableIdleTimeMillis,如果此时minEvictableIdleTimeMillis还没有赋值,则会使用默认值1800000。此时maxEvictableIdleTimeMillis=400000 < minEvictableIdleTimeMillis=1800000,所以会抛出错误。
public void setMaxEvictableIdleTimeMillis(long maxEvictableIdleTimeMillis) {if (maxEvictableIdleTimeMillis < 30000L) {LOG.error("maxEvictableIdleTimeMillis should be greater than 30000");}// 如果此时minEvictableIdleTimeMillis还没有赋值,则使用默认值1800000if (maxEvictableIdleTimeMillis < this.minEvictableIdleTimeMillis) {throw new IllegalArgumentException("maxEvictableIdleTimeMillis must be grater than minEvictableIdleTimeMillis");} else {this.maxEvictableIdleTimeMillis = maxEvictableIdleTimeMillis;}}
解决办法:
因为通过@ConfigurationProperties的方式是没办法控制设置属性的先后顺序,解决办法就是设置属性的时候先不判断两者大小,连接初始化时再判断大小。笔者已经提交了补丁修复了Bug:
https://github.com/alibaba/druid/pull/3659
最新版的Druid-1.1.22已经包含了该修复补丁。
近期热文:




