1 background
当我们遇到一个已经成型的框架的时候,作为微服务开发人员应该对涉及到的组件有一些了解,这样做并不限于包含自己的阅历,恰恰重点在与工作当中的灵活运用以及遇到问题时对解决方案的思考.另外基于动态配置全局化的高可用部署,需要做出基础依托.
2 target
面向全局化配置,达到组件依托的标准.最基础情况下洞悉热加载的思路,更深度理解配置中心管理策略和原理.
3 Source code insight
介于网路资料繁杂,这边主要站在SPRING开发者角度理解原理.如下分支
以上我已经写好一个单元测试,运行效果首先会加载springBootApplication
如下:
上图为启动环境准备加载。
环境加载按理说并没有什么特别的东西,一些默认的配置,但是我们需要知道environment这个基础类属于spring.env.core.config包下的,默认会加载项目参数和JVM参数,以及动态config的配置,这个不理解的下面会分析到。这里需要知道当我们在需要使用环境相关参数的时候可以直接依赖注入使用,默认的类为
往下
我们会发现其实这个方法还不是最基础的方法
这里总结下来就是所有的配置环境在基础run方法的时候就会加载。
我们这里关注config相关的信息,可能会适当刷过一些信息,谅解!
这里有个方法我们进去如下:
发现默认加载的applicationContext为
继续。
这里的prepareContext就会吧环境和applicationContext绑定
简单讲相关环境准备工作到这里基本就完成了。
接下来开始重点加载如下
首先加载如下方法
这里简单做个描述,serviceId为配置中心spring.application.name的value,这里为server-config,,spring为注册了springConfig的微服务单独考虑了他的加载注册机制,或许不同的地方在于与注册中心的交互。
参数如上
初始化加载还没有时间看,大概基于事件回调机制
进去看下
applicationContext子类如下
这个不是重点,不过可以作为项目cyclic 运转的事件接口,为更好的开发做准备
换句话说什么事件发生了就会回调注册该事件接口实现的方法。
然后会加载如下类(代码考不进来只能贴图,谅解)
我们先看看第一行override这个方法如下
这里的beanUtils做的COPY操作因为启动加载已经加载过。如前图。
所以得到的是之前的对象。如下:
这里的this 是调用这个方法被依赖注入的初始化环境加载的configClientProperty对象,可以翻上图看一下
Ok.
我们继续如下图
这里解释一下PREFIX=spring.cloud.config
依代码的意思就是配了spring.cloud.config.X(name|profile|label)的,在基础配置类里面就会set进去,至于这几个名词什么意思下面会讲到
继续
以上方法我们可以关注一下,依稀记得之前写eureka的时候我说过restTemplate可以用
clientHttpRequestFactory作为访问前的请求修饰。稍微过一下最后的拦截器吧,如下:
他写的比较简单,但是可以作为我们灵活开发应用的基础。这里主要也是做修饰
设计模式 : 责任链
继续往下:
以上是个比较重要的方法如下:
简单说下这个方法做了什么事情,如果在前几步spring.config.cloud.label配置有值的话这里PATH就会为/{name}/{profile}/{label},没有的话为/{name}/{profile},应该好理解吧。
我们直接看他的调用
请求为GET
拼出来大概为http://10.0.3.96:9020/server-gateway/dev
这个在SPTING官网有个配置服务器发布的rest endpoint,这里用的是{application}-{profile}.properties的形式,这个可以在任何网站上查到,不赘述。
看下结果如下
这里返回了两个YML文件,原理得看服务端代码,不过从官网提供的数据了解到。
每次都会返回×-application-×.(properties|yml),然后在根据不同的profile返回application-profile.(properties|yml)。
这里小结一下,这里的命名规范按照profile label application 来就行了,服务端会根据你的三个参数发布不同的端点,客户端直接可以通过GET访问类似下图
这里返回的就是JSON格式的信息了,也直接可以用ConfigurationManagement解析某个属性
另外补充一下,我们通过源码分析貌似发现和包没有什么关系,也就是你写到一个包里面也可以,视觉习惯不允许我们这么做,但是我们要知道这个梗。
继续:
拿到服务器端参数也就是远程配置参数之后这里会做一个MAP处理最后放到CompositePropertySource里面,顾名思义聚合属性源,我们看一眼如下,现在还只有三个源。
那就看它是怎么聚合的,往下
回到刚刚调用的方法locator
往下83行为重点方法,先解释下,这里会吧环境初始化属性拿出来,然后走下
以上第一行把composite(CompositePropertySource)插入当前环境属性变量,貌似我们还记得这个变量在之前locator的时候就已经吧远程配置的参数设置进去了。这里得出的结果是两者合并,这也就体现了为什么要命名为composite了,我们看一眼如下
到这里主要的配置就加载完了。
然后加载
方法如下
其中这个里面就是默认Configuration接口实现类。
到现在为止参数如下:
Archaius 加载的时候会引入configuration的概念,很明显一个CONFIGURATION装了所有的环境配置,本地配置和远程CONFIG配置。继续、
这里我们会这里是用ConcurrentCompositeConfiguration封装上面的configruation
,这里在NEW URLConfigurationSource的时候会加载如下代码:
这段代码简单说下,archaius用来解析本地系统动态配置文件的,上面写的URL但是你会发现无法解析HTTP协议因为并没有解析HTTP URL的类加载器,看到这里也解决了我一个困惑,Archaius 动态属性是没法或许远程参数的:
动态配置原理就是下面startpolling方法
他会启动一个定时器定时去检查configlist对应路经文件的改动,发现发生了变化立即重新加载
瞄一眼如下:
这里会启动一个定时器默认60秒去查一次数据对比,具体逻辑感兴趣的可以去看下。
继续
很明显倒数3行给ConfigurationManager.instance 赋值了我们的ConcurrentCompositeConfiguration而我们的ConcurrentCompositeConfiguration对象又加载了全局所有配置,所以可以直接ConfigurationManager.instance.getProperty拿服务端参数,注意服务端参数还没有做热加载
最后会有一个
过程
support
如下:
然后源码走完了接着到了单元测试
当前行进去
我们打开concurrentCompositeConfiguration的实现进去可以看到如下
一目了然。遍历所有的CONFIGURATION,拿出一个包含key为server.port的configuration拿出其value,
然后我们看看DynamicPropertyFactory是什么效果
里面我们会看到这个实现类,如果我没记错的化里面在之前已经包装了ConcurrentCOmpositeConfiguration ,节约篇幅这里就不DEBUG了 一样的逻辑
只是加了一个动态时间定时发布,但是暂时还没有开分布式着一块。
分析完以上我们至少可以总结如下:
1 spring config 配置服务中心默认会提供关于不同关键词所提供的endpoint ,以供restfull访问,返回的JSON格式
2 系统全局配置都由concurrentCompositeConfiguration管理 ,已经给ConfigurationManagement的静态变量(instance)赋值,所以可以直接调用静态方法拿到
3 archaiusAutoConfiguration 提供的 archaius 定时任务可以做到动态属性配置,默认加载classpath:/config.properties文件,可以设置多个文件和定时的时间通过外部属性加入
例如-Darchaius.configurationSource.additionalUrls=xxx
4 DynamicPropertyFactory 底层也是concurrentCompositeConfiguration
5 archaius这块 ,本地创建config.properties 或者指定加载某文件,然后不重启项目修改本地配置,重新compile 即可看到效果 代码如下:
未来展望:通过源码分析我们知道spring config 是不能支持远程服务器属性热加载的,本地我也有测试,但是通过Archaius 组件可以实现本地热加载,原理上面已经分析过了。既然是分布式系统,统一注册中心,我们也希望能够修改一处时自动通知并修改相关联的微服务系统,增强拓展性,可维护性,希望以此为基础寄托。
致敬spring开源团队.