zoukankan      html  css  js  c++  java
  • alibaba druid 在springboot start autoconfig 下的bug

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


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

    背景

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

    发现、分析过程

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

    我们现在处于.net专java过程中,特殊场景下新java系统需要连接两个数据源,默认连接mysql数据源,但是有时候还需要查询sqlserver数据源来获取一些兼容性数据。
    所以,在配置第二个数据源的时候,系统load就报错。

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

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

    如果不是springboot,一般都会自己来初始化所有的属性。从配置文件加载配置,然后手动设置DruidDataSource bean。

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

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

    大概意思是说,'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

    https://www.cnblogs.com/wangiqngpei557/p/7136455.html?utm_source=itdadao&utm_medium=referral

  • 相关阅读:
    Mybaits的10种通用的写法
    你清楚这几个Spring常用注解吗?
    面试时如何介绍自己
    Spring加载加密的配置文件
    MySQL主从复制
    CentOS7安装和配置MySQL
    Druid监控页面配置与使用
    Shiro配置URL过滤
    基于注解的Dubbo服务配置
    Java中常用的加密算法小结
  • 原文地址:https://www.cnblogs.com/softidea/p/11018102.html
Copyright © 2011-2022 走看看