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

相關文章
相關標籤/搜索