nacos中配了一個數字,springboot取回來怎么變了
背景
對于java開發人員來說,nacos想必不陌生了,我們這邊是拿來做配置中心為主。我這邊的習慣用法是,在bootstrap.yml中配置nacos相關的配置、profile:

然后呢,可以看到,nacos是支持啟用或者不啟用的,如果為true,就會使用nacos上的配置;我本地開發的時候,隨時會把配置改來改去,我一般設置為false,不啟用,此時就會使用本地的application-dev.yml中的配置。
開發得差不多了,再切換成使用nacos中配置,測一下就差不多了。
{
"name": "spring.cloud.nacos.config.enabled",
"type": "java.lang.Boolean",
"description": "enable nacos config or not.",
"defaultValue": true
},
結果呢,最近遇到個小問題。如下所示,有個appCode,這個配置項,是一個應用的唯一編碼,開發環境配置如下,不論使用啟用nacos,這塊在程序中獲取的配置都是正確的。

對應的spring中接收配置的代碼如下:

然后,轉到測試環境后,換成了如下配置,也就是code變了,結果程序中取出來,就成了:


appCode怎么是266啥的,我當時和測試同事仔細檢查了下,確認沒配置錯誤。
那,這是咋回事呢
定位過程
排除nacos問題
由于是測試環境,網絡都是通的,我直接在本地ide連測試環境,復現了下問題,抓了個本地和nacos之間的包:

看了下一點問題沒有。接下來就是看代碼怎么處理的了。
propertysouece
先弄個field斷點,看看寫入時候的棧。其實如果對nacos熟悉的話,也可以正向排查,從nacos獲取到配置后的處理過程開始看,我這里就先按照反向流程來。

然后就看到斷點停住了,進來的值確實不對:

把棧往上翻了翻,發現是在創建bean的過程中調用這塊方法的。我們知道,一個bean的創建,一般會有:調用構造函數--》注入field的值(比如那些設置了@autowired的field、或者是我這這種注解了@ConfigurationProperties(prefix = "app")的,等等)--》調用init方法或afterpropertiesSet方法等。
此時,我的棧就處于第二步,此時bean已經通過構造函數弄出來了,正在注入ConfigurationProperties相關屬性的值。
翻到上面的caller棧,發現下面這個地方,獲取到的bound字段的值已經是2開頭,是錯的了,那看來就是這個地方,取到了錯誤的值,那就這里打個斷點再來。

這次從上面斷點進來后,發現進入了一個findProperty的方法,如下,主要就是根據app.app-list-need-query-todo-num[0].app-code這么個屬性,要獲取到對應的value,這也正常,要先獲取到值,才能設置到bena里。

可以看到,獲取property主要是從propertySource中獲取。這個property是啥類型呢,
org.springframework.boot.context.properties.source.SpringIterableConfigurationPropertySource,里面有另一個propertySource字段,這個字段的類型是:org.springframework.cloud.bootstrap.config.BootstrapPropertySource,而下圖可看到,BootstrapPropertySource中的delegate字段,才指向真正的propertySource,即NacosPropertySource

這里我們跟進去后,發現,property的格式不太一致,nacos中的存儲都是駝峰格式,而這里獲取配置的property是中劃線格式,所以這次是獲取不到的。

不過,spring做了兼容,會對property進行轉換,變成駝峰格式再來找一次:

這次能找到了,但找到的是錯誤的,所以,我們還得看看nacosPropertySource中的值,為啥是錯的。
nacosPropertySource中的值,存放在source這個字段中,類型是LinkedHashMap,我們就看看這個map是什么時候賦值的。

我們發現,這個source應該是構造函數時候,設置進來的,如下:

找了下調用這個構造函數的地方,如下,這里就會去先獲取nacos中的配置,然后調用構造函數,我們看了下,發現下面紅框的propertySource中已經是錯誤的了,得再打個斷點跟進去

loadNacosData
下面這個方法中,先就是去nacos服務端獲取到配置,然后再進行解析,可以看到,從服務端取到的是對的,那就是解析的問題,繼續跟進去:

由于nacos中配置支持多種格式,如下圖所示,有多種解析器,我們這邊是yaml,類型是:org.springframework.boot.env.YamlPropertySourceLoader,這個loader是spring-boot自帶的:


這個org.springframework.boot.env.YamlPropertySourceLoader實際自己并不能獨立完成yaml格式文件的解析,而是依賴另一個jar包:
<dependency>
<groupId>org.yaml</groupId>
<artifactId>snakeyaml</artifactId>
<version>1.30</version>
<scope>compile</scope>
</dependency>


yaml
yaml這個具體的解析邏輯就比較復雜了,可以使用條件斷點,不然很難debug。

下面可以看到,如果這個字符串,是0開頭,就認為是8進制了,且會去掉開頭的0,如:01011003,由于首位為0,認為是8進制,然后去掉符號位,值為:1011003

然后把1011003轉成8進制數字,變成了266755



為啥開發環境時的010190那個code沒轉成8進制了,可能是轉換失敗了(但我發現改成010190后,沒走上面那段邏輯,里面yaml解析挺復雜,就沒繼續跟了)。
解決方式
由于是進制轉換的問題,我這里的解決方式是,直接在nacos用字符串來配置,避免這些數字轉換問題。


浙公網安備 33010602011771號