背景
对于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>
- 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用字符串来配置,避免这些数字转换问题。
来源:程序园用户自行投稿发布,如果侵权,请联系站长删除
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作! |