spring cloud 篇之配置中心
一. 概述
在 spring cloud 全家桶中配置中心使用的是 spring-cloud-config 包,其提供了配置拉取服务,然而并没有提供配置管理能力,而是通过插件来进行管理;例如,git 等;所以大体的架构图如下:
从这个图来看,感觉 config-server 没体现出它的价值。之所以需要 config-server 这个存在,主要考虑几个点,也是官方所列出来的特性:
提供 http 协议的请求方式
对元数据进行加密解密
更好的集成到 spring boot 应用
如果客户端在运行当中,git 的配置发生了变更了,想要达到客户端拿到变更后的数据,则需要额外的组件来配置,官方提供的建议是使用 spring-cloud-bus 以及 spring-config-monitor 来配合。具体的交互如下图:
至于加密的,则在使用以及源码层面解读时进行介绍。
二. 原理
2.1 服务端
当服务端启动时,对配置内容解析有两种方式:
饿汉模式,即立即将配置资源加载到服务端内部;
懒汉模式,由客户端要拉取时,才加载到服务端内部;
其配置资源数据结构 org.springframework.cloud.config.environment.Environment 是:
即意味着服务端是基于这个最小数据维度进行管理配置信息;
2.2 客户端
当客户端启动时,向服务端拉取配置信息,即上面的 Environment 信息,然后加载到客户端的环境变量中,供使用;
2.3 总结
配置中心平台相对交互简单,服务端对配置进行管理;客户端向服务端拉取配置信息;
三. spring-cloud-config 实现原理
在 spring boot2.6.*版本,是使用 EnvironmentPostProcessor 接口所对应的实现 ConfigDataEnvironmentPostProcessor 类来实现;
而一旦配置发生了变更,通过事件形式去触发,最终会调用类去重新刷新配置。
服务端主要是 ConfigDataEnvironmentPostProcessor 类来处理
客户端主要是 ConfigFileApplicationListener 类向
该接口的实现类主要是对 spring environment 对象进行前置,例如往里面增加额外的环境变量,以及修改环境变量等操作;那么什么时候会被出发呢?主要是由事件监听等方式去触发,例如 spring boot 启动时会抛出 ApplicationEnvironmentPreparedEvent 事件。
2.1 服务端
其底层原理是运用了 EnvironmentRepository 接口来获取对应的配置信息,从而实现多样化的配置中心;
2.1.1 配置管理
当我们直接引用 jar,如下代码。默认是使用的 git 进行管理配置;
所以,config-server 本身是不提供配置管理的。
我们可以在配置文件中配置 git 地址即可;具体都配置哪些内容,可查阅 MultipleJGitEnvironmentProperties 类即可;这里可以配置多个 git 地址。
当容器启动时会向 git 仓库拉取文件到本地,具体流程如下:
2.1.2 http 服务
所提供的服务能力,主要可以查阅三个类
EncryptionController 提供加密解密服务
EnvironmentController 提供了获取对应的以 Environment 类承载的配置信息对象
ResourceController 提供了获取对应的以字符串类承载的配置信息对象
下面以当 EnvironmentController 类的接收到客户端向服务端拉取配置请求的大体流程如下:
这里重点介绍匹配到对应的 git 仓库以及对应的目录规则;
匹配 git 仓库:将配置的 git url 中的{application}、{profile}、{label}关键进行替换成对应值,得到的才是真正的 git 仓库地址;
获取到对应目录:假设 git 仓库拉取到本地的路径,/data/git/config
如果我们配置了 searchPath 值,则会尝试替换{application}、{profile}、{label}得到 git 拉到本地下的最终的目录。假设是 application/profile/label 这个目录,那么最终的目录是/data/git/config/application/profile/label/
如果没有配置,则默认是/data/git/config,那么最终的目录是/data/git/config
这里面的流程有点复杂,尤其是 ConfigDataEnvironmentPostProcessor 工具对 ConfigurableEnvironment 对象中的是 spring.config.name,spring.config.location 等所指定的路径。但是不难可以猜测的出,其最终会流转到 ConfigDataLocationResolver 接口所对应的实现类去拉取文件资源,流程图如下:
上面的流程图,只是关键的流程,不涉及很多细节;从上面的流程图,还可以再次精简,流程图如下:
这里有一个疑惑点:客户端端每次启动时向配置服务拉取时,都会重新加载解析文件麽?
虽然有点概览了,但是细节不能一一阐述到位,就举个场景来说明:
2.1.3 解密
其通过对 EnvironmentEncryptor 接口的实现类去对属性对象的含有{cipher}前缀的 value 进行解密;
具体可以查看 CipherEnvironmentEncryptor 对象;逻辑都很清晰;
2.1.4 配置实时性
这里主要是讲述的是一旦 git 仓库中的配置发生了变更,那么 config-server 中的配置就会发生变更;然而这个实时性是懒加载模式,有客户端主动拉取时,config-server 才会重新向 git 仓库拉取地址;
这里主要是依赖 spring cloud 中的 @RefreshScope 注解的功能特性;但是我们需要添加额外的依赖包,如下:
然后在 git 中添加 webhook,url 如下:
当 git 回调告知配置已经变更时,就会抛出 RefreshRemoteApplicationEvent 事件:会有一个监听器监听并做其对应的处理逻辑,流程图如下:
EnvironmentPostProcessor 接口的实现类很多,这里主要对一些关键类进行简单说明:
ConfigFileApplicationListener 加载本地 spring.config.location 等指定的配置文件信息
ConfigDataEnvironmentPostProcessor 加载 ConfigData 配置资源到 Environment 对象中去,这一块可以参考上面几个流程图;
DecryptEnvironmentPostProcessor 对 environment 加密的配置信息进行解密
2.2 客户端
客户端则直接引入一下 jar 包,即可向服务端拉取配置信息:
当容器启动时,会触发调用 ConfigServicePropertySourceLocator 对象去向 config-server 拉取配置信息;
配置实时性
需要额外引入总线
这样子才能监听到 config-server 抛出来的刷新 RefreshRemoteApplicationEvent,具体细节可以查看服务端的【配置实时性】小节内容;
三. 架构
配置中心,一般情况只有在服务启动时,以及配置变更时,让客户端重新拉取配置;所以,在大部分时间里是没有被调用的;在做搭建配置中心时,一般主要考虑流量激增场景,是否能承载的住;而不用考虑其高可用性;
默认的 spring cloud config server 使用的 git server,其瓶颈点主要是在 git server;当有上万台微服务启动时,会大量的流量流到 git server 服务器,很容易 git server 出现宕机现象;那么就会出现客户端系统启动失败;
所以要想达到一个稳定的环境,则进行一定的优化,例如,
取消定时先 git server 拉取最新配置等;
增加缓存特性,减少对 git 的访问;那么意味着需要添加代码才能达到该特性,目前源码中是没有实现该特性的;
config-server 启动时,直接拉取 git 仓库到服务端所在的文件服务系统中;
针对该 spring-cloud-config 的个人想法:
局限性:
配置实时性,依赖的内容太多,引入总线,以及总线对应的 MQ;以及 git 的 webhook。
缺少缓存特性
高并发低效
优化想法:
将配置仓库与配置管理完全独立化,增加配置同步模块
配置仓库,可以往里面增加缓存特性。当配置同步模块中发现有信息变更时。会通知到配置仓库,让配置仓库模块修改对应的变更内容;
客户端,在启动时会向服务端拉取配置信息。同时起一个定时任务,定时向服务端发送询问,配置是否发生了变更,一旦发生了变更,则重新拉取配置信息,并替换当前的配置内容;
四. 使用
4.1 服务端
application.yml
4.2 客户端
bootstrap.yml
版权声明: 本文为 InfoQ 作者【邱学喆】的原创文章。
原文链接:【http://xie.infoq.cn/article/d96f6e5f0ebf993a374ba6020】。文章转载请联系作者。
评论