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
總結
突然能明白,其實有關于開源的好處,你已經獲益了。可以一起參與使用,一起參與發現問題,一起參與fixbug。這也許就是linux、git這類優秀藝術品背后的核心技術價值觀。
github 地址:https://github.com/Plen-wang
文章列表