alibaba druid 在springboot start autoconfig 下的bug

本文涉及的产品
RDS SQL Server Serverless,2-4RCU 50GB 3个月
推荐场景:
云数据库 RDS SQL Server,基础系列 2核4GB
简介:

* 背景

* 发现、分析过程

* 总结


背景

最近在使用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的创建。这看起来好像没什么问题。

1
2
3
4
5
@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'最小存活时间。


第一反应肯定是配置错了,检查配置。

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

好像没错啊,然后在debug下,问题同样出现。minEvictableIdleTimeMillis属性和maxEvictableIdleTimeMillis属性的前后顺序好像也没问题。试试看的心里,将两个属性前后顺序交换下,问题还是出现。


蒙蔽状态~_~。


感觉这个问题有点诡异了,时间要紧,直接找到报错的地方,进行源码跟踪。

上 github search alibaba druid 首页,直接gith clone下来,定位到错误提示的位置。


全局查找的时候有两处有这个exception的throw。

一: init 方法

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

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


二:setMaxEvictableIdleTimeMillis 方法

1
2
3
4
5
6
7
8
9
10
11
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。


看下这个值哪里来的。

1
protected  volatile  long   minEvictableIdleTimeMillis  = DEFAULT_MIN_EVICTABLE_IDLE_TIME_MILLIS;
1
2
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获取进来

1
2
3
4
5
6
7
@Data
@EqualsAndHashCode
@ConfigurationProperties (prefix =  "ecommon.order.druid" )
public  class  SqlServerDruidConfig {
     private  Long minEvictableIdleTimeMillis;
     private  Long maxEvictableIdleTimeMillis;
}

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
@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


 本文转自 王清培 51CTO博客,原文链接:http://blog.51cto.com/wangqingpei557/1945480,如需转载请自行联系原作者





相关实践学习
使用SQL语句管理索引
本次实验主要介绍如何在RDS-SQLServer数据库中,使用SQL语句管理索引。
SQL Server on Linux入门教程
SQL Server数据库一直只提供Windows下的版本。2016年微软宣布推出可运行在Linux系统下的SQL Server数据库,该版本目前还是早期预览版本。本课程主要介绍SQLServer On Linux的基本知识。 相关的阿里云产品:云数据库RDS&nbsp;SQL Server版 RDS SQL Server不仅拥有高可用架构和任意时间点的数据恢复功能,强力支撑各种企业应用,同时也包含了微软的License费用,减少额外支出。 了解产品详情:&nbsp;https://www.aliyun.com/product/rds/sqlserver
相关文章
|
3月前
|
SQL 监控 druid
springboot-druid数据源的配置方式及配置后台监控-自定义和导入stater(推荐-简单方便使用)两种方式配置druid数据源
这篇文章介绍了如何在Spring Boot项目中配置和监控Druid数据源,包括自定义配置和使用Spring Boot Starter两种方法。
|
2月前
|
druid Java Maven
|
3月前
|
Java Spring
springboot 集成 swagger 2.x 和 3.0 以及 Failed to start bean ‘documentationPluginsBootstrapper‘问题的解决
本文介绍了如何在Spring Boot项目中集成Swagger 2.x和3.0版本,并提供了解决Swagger在Spring Boot中启动失败问题“Failed to start bean ‘documentationPluginsBootstrapper’; nested exception is java.lang.NullPointerEx”的方法,包括配置yml文件和Spring Boot版本的降级。
springboot 集成 swagger 2.x 和 3.0 以及 Failed to start bean ‘documentationPluginsBootstrapper‘问题的解决
|
4月前
|
缓存 NoSQL Java
SpringBoot的三种缓存技术(Spring Cache、Layering Cache 框架、Alibaba JetCache 框架)
Spring Cache 是 Spring 提供的简易缓存方案,支持本地与 Redis 缓存。通过添加 `spring-boot-starter-data-redis` 和 `spring-boot-starter-cache` 依赖,并使用 `@EnableCaching` 开启缓存功能。JetCache 由阿里开源,功能更丰富,支持多级缓存和异步 API,通过引入 `jetcache-starter-redis` 依赖并配置 YAML 文件启用。Layering Cache 则提供分层缓存机制,需引入 `layering-cache-starter` 依赖并使用特定注解实现缓存逻辑。
1253 1
SpringBoot的三种缓存技术(Spring Cache、Layering Cache 框架、Alibaba JetCache 框架)
|
4月前
|
Java 微服务 Spring
SpringBoot+Vue+Spring Cloud Alibaba 实现大型电商系统【分布式微服务实现】
文章介绍了如何利用Spring Cloud Alibaba快速构建大型电商系统的分布式微服务,包括服务限流降级等主要功能的实现,并通过注解和配置简化了Spring Cloud应用的接入和搭建过程。
SpringBoot+Vue+Spring Cloud Alibaba 实现大型电商系统【分布式微服务实现】
|
5月前
|
监控 druid Java
spring boot 集成配置阿里 Druid监控配置
spring boot 集成配置阿里 Druid监控配置
323 6
|
4月前
|
druid Java 数据库连接
SpringBoot项目整合MybatisPlus持久层框架+Druid数据库连接池,以及实现增删改查功能
SpringBoot项目整合MybatisPlus和Druid数据库连接池,实现基本的增删改查功能。
399 0
|
6月前
|
druid Java 关系型数据库
在Spring Boot中集成Druid实现多数据源有两种常用的方式:使用Spring Boot的自动配置和手动配置。
在Spring Boot中集成Druid实现多数据源有两种常用的方式:使用Spring Boot的自动配置和手动配置。
993 5
|
6月前
|
druid Java 数据库
spring boot 整合 druid(深入浅出)
spring boot 整合 druid(深入浅出)
233 0
|
6月前
|
Java
SpringBoot启动报错:Unable to start LiveReload server【已解决】
SpringBoot启动报错:Unable to start LiveReload server【已解决】
321 0