Bug背景:
公司SpringBoot項目最近需要用到分布式鎖這個東西,分布式鎖目前實現有很多種,以Redis來實現的話有StringRedisTeamplate.setIfAbsent,Redssion的RedssionClient以及Gitee上的RedisLock都可以用作分布式鎖,實現上差別挺大,項目以前老寫法使用下面這個:
文檔上介紹是在事務或管道中執行返回為空
@Nullable
Boolean setIfAbsent(K key, V value, long timeout, TimeUnit unit);
而最近又有同事引入了Redision想用Redssion的API來實現分布式鎖,但是沒有去配置,於是吃螃蟹的我嘗試去配置了相關客戶端,用了客戶端的可重入鎖,但是后來詭異的bug發生了,之前用的setIfAbsent方法返回為空了,WTF!!!Debug也很難跟進,里面有大量的回調方法、匿名表達、異步操作,面向搜索引擎編程這次也失靈了,只定位到了是Redssion而沒有解決方案,叫人頭禿。
解決方案
不過最近讀了下SpringBoot啟動的源碼,猜測了下會不會是Boot自動裝配的問題,既然是引入了Redssion導致的,那么於是找到了Redssion啟動類的代碼,就是下面這個文件:
啟動類工廠文件spring.factories自動裝配的類之一,SpringBoot啟動時會加載所有META-INF文件夾下的spring.factories文件,找到要裝配的類,RedssionAutoConfiguration會根據@ConditionalOnMissingBean這個注解順序裝配以下幾個bean:
RedissonClient——>RedissonConnectionFactory——>StringRedisTemplate
問題就顯而易見了,如果配置了RedissonClient,沒有配置RedissonConnectionFactory,就會以RedssionClient里的連接配置去構建了RedisConnectionFactory,而項目中我又剛好手動去配置了一個RedissonClient,就發生了這么詭異的問題,Redssion底層應該是大量Netty的API,影響了原來的邏輯從而導致了返回為空,當然這個目前還沒有驗證,后續有機會再深入Debug探討一下,解決方案下面兩個都可以(可以同時配置,任意一個也會生效)。
- 在啟動類注解@SpringBootApplication的exclude屬性里添加{RedissonAutoConfiguration.class}這個屬性,會讓SpringBoot根據其他條件自行構建連接池和Redis操作模板,避免StringRedisTeamplate受到RedssionClient的影響。
- 在項目中手動配置RedisConnectionFactory,我配置的是LettucePoolingClientConfiguration這個連接工廠,主要是池大小和Redis實例地址,根據需要自行配置,網上很多案例可供參考,在此就不過多贅述了。
總結
SpringBoot自動裝配機制在開發過程中使用起來很方便,引入相應依賴便能夠開箱即用,對應用上層屏蔽了很多細節,但是一旦出現類似問題而又不報錯時便難以定位,還是我們需要多探究一下框架的細節,也是在進階過程中要掌握的吧。