暂无图片
暂无图片
1
暂无图片
暂无图片
暂无图片

Druid的一个Bug

codeImport 2020-04-19
5456

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,即可能被关闭的连接数量
// 或者空闲时间大于maxEvictableIdleTimeMillis
   if (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=400000
master.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还没有赋值,则使用默认值1800000
if (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已经包含了该修复补丁。


近期热文:

使用nacos做为istio的服务发现

定位线上问题不求人

同一个事务操作多个数据库


文章转载自codeImport,如果涉嫌侵权,请发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

评论