alibaba druid 在springboot start autoconfig 下的bug

alibaba druid 在springboot start autoconfig下的bug

标签(空格分隔):druid springboot start autoconfigjava


  • 背景
  • 发现、分析过程
  • 总结

背景

最近在使用alibaba druid进行多数据源链接的时候无心中发现一个小bug,已经提交github issue 官方已经fix。issue 地址:https://github.com/alibaba/druid/issues/1796mysql

发现、分析过程

咱们使用的java开发框架是封装好的。框架对数据源的支持是master、slave架构的,就是能够一组多从数据源,内部会自动进行主从写入、查询切换。linux

咱们如今处于.net专java过程当中,特殊场景下新java系统须要链接两个数据源,默认链接mysql数据源,可是有时候还须要查询sqlserver数据源来获取一些兼容性数据。
因此,在配置第二个数据源的时候,系统load就报错。git

咱们使用springboot框架,datasource config 基于springboot properties进行配置。而后使用configuration 进行自动druid daasource bean的建立。这看起来好像没什么问题。github

@Bean(name = "dataSource")
    @ConfigurationProperties(prefix = "ecommon.order.druid")
    public DataSource getOrderDataSource() {
       return new DruidDataSource();
    }

若是不是springboot,通常都会本身来初始化全部的属性。从配置文件加载配置,而后手动设置DruidDataSource bean。spring

没多想,就直接用这种方式使用了。可是在启动的时候,初始化bean的时候就报错了。sql

'maxEvictableIdleTimeMillis' threw exception; nested exception is java.lang.IllegalArgumentException: maxEvictableIdleTimeMillis must be grater than minEvictableIdleTimeMillisspringboot

大概意思是说,'maxEvictableIdleTimeMillis'最大存活时间必须大于'minEvictableIdleTimeMillis'最小存活时间。架构

第一反应确定是配置错了,检查配置。框架

##一个链接在池中最小生存的时间(ms)
ecommon.order.druid.minEvictableIdleTimeMillis=300000
##一个链接在池中最大生存的时间(ms)
ecommon.order.druid.maxEvictableIdleTimeMillis=600000

好像没错啊,而后在debug下,问题一样出现。minEvictableIdleTimeMillis属性和maxEvictableIdleTimeMillis属性的先后顺序好像也没问题。试试看的内心,将两个属性先后顺序交换下,问题仍是出现。

蒙蔽状态~_~。

感受这个问题有点诡异了,时间要紧,直接找到报错的地方,进行源码跟踪。
上 github search alibaba druid 首页,直接gith clone下来,定位到错误提示的位置。

全局查找的时候有两处有这个exception的throw。
一: init 方法

public void init() throws SQLException {
            if (maxEvictableIdleTimeMillis < minEvictableIdleTimeMillis) {
                throw new SQLException("maxEvictableIdleTimeMillis must be grater than minEvictableIdleTimeMillis");
            }

为了便于阅读,我删掉了init中对咱们分析问题来讲无用的代码。

二:setMaxEvictableIdleTimeMillis 方法

public void setMaxEvictableIdleTimeMillis(long maxEvictableIdleTimeMillis) {
        if (maxEvictableIdleTimeMillis < 1000 * 30) {
            LOG.error("maxEvictableIdleTimeMillis should be greater than 30000");
        }
        
        if (maxEvictableIdleTimeMillis < minEvictableIdleTimeMillis) {
            throw new IllegalArgumentException("maxEvictableIdleTimeMillis must be grater than minEvictableIdleTimeMillis");
        }
        
        this.maxEvictableIdleTimeMillis = maxEvictableIdleTimeMillis;
    }

这两个方法逻辑都比较简单,init初始化的时候会检查这两个bean属性的值。setMaxEvictableIdleTimeMillis,设置这个最大存活时间的时候有一个检查。若是你的最大存活时间小于最小存活时间直接报错。

因此直接在这两个地方打上断点,而后debug,在跟踪下变量的值基本就知道问题在哪里了。

经过debug,发现直接new DruidDataSource()使用数据源,不会走到init方法。不会进入到触发报错的地方。貌似个人代码路径应该不会产生这个检查。继续运行,看setMaxEvictableIdleTimeMillis方法什么状况。

发现问题了,在进行setMaxEvictableIdleTimeMillis方法的时候,minEvictableIdleTimeMillis属性的值是1800000。

看下这个值哪里来的。

protected volatile long  minEvictableIdleTimeMillis  = DEFAULT_MIN_EVICTABLE_IDLE_TIME_MILLIS;
public static final long  DEFAULT_MIN_EVICTABLE_IDLE_TIME_MILLIS = 
1000L * 60L * 30L;

minEvictableIdleTimeMillis 属性有一个default值。单位是ms(millisecond),因此这里的default 值是30m(minute)分钟。

咱们配置的是600000ms,因此比1800000小。可是这个就奇怪了,咱们明明设置了minEvictableIdleTimeMillis 参数。为何没起做用,一会儿就想到是否是顺序问题。

而后继续debug,先绕过这个检查,看是否是setMinEvictableIdleTimeMillis 方法在setMaxEvictableIdleTimeMillis 方法以后执行,致使这个检查和配置顺序冲突。

debug下来,确实是这个问题。而后就比较好奇,咱们如今所使用的这个框架帮咱们封装了druid的时候是怎么处理的,经过查看框架源码,里面有一个hashcode的排序,解决了这个问题。(果真老司机,高手)这里就不展开了。

那么咱们若是解决这个问题,其实很简单,知道问题在哪里绕过去仍是很简单的。

一:先把配置的minEvictableIdleTimeMillis、maxEvictableIdleTimeMillis获取进来

@Data
@EqualsAndHashCode
@ConfigurationProperties(prefix = "ecommon.order.druid")
public class SqlServerDruidConfig {
    private Long minEvictableIdleTimeMillis;
    private Long maxEvictableIdleTimeMillis;
}

二:而后本身先设置一下这两个属性,springboot autoconfig 的时候就不会出错

@Autowired
    private SqlServerDruidConfig druidConfig;

    @Bean(name = "dataSource")
    @ConfigurationProperties(prefix = "ecommon.order.druid")
    public DataSource getOrderDataSource() {

        DruidDataSource dataSource = new DruidDataSource();

        /**setMinEvictableIdleTimeMillis须要先设置*/
        dataSource.setMinEvictableIdleTimeMillis(druidConfig.getMinEvictableIdleTimeMillis());
        dataSource.setMaxEvictableIdleTimeMillis(druidConfig.getMaxEvictableIdleTimeMillis());

        return dataSource;
    }

    @Bean
    public SqlServerDruidConfig getDruidConfig() {
        return new SqlServerDruidConfig();
    }

问题到这里分析就结束了。这个小bug,alibaba druid 已经fix。
若是你对如何fix感兴趣,请参看druid 有关于autoconfiger修复的代码:
https://github.com/lihengming/druid/commit/ca13e8ff5a78c83f953fa8fb320c56be223219e1

总结

忽然能明白,其实有关于开源的好处,你已经获益了。能够一块儿参与使用,一块儿参与发现问题,一块儿参与fixbug。这也许就是linux、git这类优秀艺术品背后的核心技术价值观。

github 地址:https://github.com/Plen-wang

相关文章
相关标签/搜索