相信大家都知道,每个项目中会有一些配置信息放在一个独立的properties文件中,比如application.properties。这个文件中会放一些常量的配置,比如数据库连接信息、线程池大小、限流参数。
在传统的开发模式下,这种方式很方便,一方面能够对配置进行统一管理,另一方面,我们在维护的时候很方便。
但是随着业务的发展以及架构的升级,在微服务架构中,服务的数量以及每个服务涉及到的配置会越来越多,并且对于配置管理的需求越来越高,比如要求实时性、独立性。
另外,在微服务架构下,会涉及到不同的环境下的配置管理、灰度发布、动态限流、动态降级等需求,包括对于配置内容的安全与权限,所以传统的配置维护方式很难达到需求。
因此,就产生了分布式配置中心。
- 传统的配置方式不方便维护
- 配置内容的安全和访问权限,在传统的配置方式中很难实现
- 更新配置内容时,需要重启
配置中心的工作流程
图11-1
Spring Boot的外部化配置
=================
在本次课程中,我们会Zookeeper集成到Spring Boot的外部化配置中,让用户无感知的使用配置中心上的数据作为数据源,所以我们需要先了解Spring Boot中的外部化配置。
Spring Boot的外部化配置是基于Environment来实现的,它表示Spring Boot应用运行时的环境信息,先来看基本使用
Environment的使用
- 在spring boot应用中,修改aplication.properties配置
1 | properties复制代码key=value |
- 创建一个Controller进行测试
1 | java复制代码@RestController |
@Value注解使用
在properties文件中定义的属性,除了可以通过environment的getProperty方法获取之外,spring还提供了@Value注解,
1 | java复制代码@RestController |
spring容器在加载一个bean时,当发现这个Bean中有@Value注解时,那么它可以从Environment中将属性值进行注入,如果Environment中没有这个属性,则会报错。
Environment设计猜想
Spring Boot的外部化配置,不仅仅只是appliation.properties,包括命令行参数、系统属性、操作系统环境变量等,都可以作为Environment的数据来源。
- @Value(“${java.version}”) 获取System.getProperties , 获取系统属性
- 配置command的jvm参数,
-Denvtest=command
,然后通过@Value("${envtest}")
图11-2
- 第一部分是属性定义,这个属性定义可以来自于很多地方,比如application.properties、或者系统环境变量等。
- 然后根据约定的方式去指定路径或者指定范围去加载这些配置,保存到内存中。
- 最后,我们可以根据指定的key从缓存中去查找这个值。
扩展Environment
我们可以自己扩展Environment中的数据源,代码如下;
其中,EnvironmentPostProcessor:它可以在spring上下文构建之前可以设置一些系统配置。
CusEnvironmentPostProcessor
1 | java复制代码public class CusEnvironmentPostProcessor implements EnvironmentPostProcessor { |
custom.properties
在classpath目录下创建custom.properties文件
1 | properties复制代码name=mic |
spring.factories
在META-INF目录下创建spring.factories文件,因为EnvironmentPostProcessor的扩展实现是基于SPI机制完成的。
1 | properties复制代码org.springframework.boot.env.EnvironmentPostProcessor=\ |
TestController
创建测试类,演示自定义配置加载的功能。
1 | java复制代码@RestController |
总结
通过上面的例子我们发现,在Environment中,我们可以通过指定PropertySources来增加Environment外部化配置信息,使得在Spring Boot运行期间自由访问到这些配置。
那么我们要实现动态配置中心,无非就是要在启动的时候,从远程服务器上获取到数据保存到PropertySource中,并且添加到Environment。
下面我们就开始来实现这个过程。
Zookeeper实现配置中心
在本小节中,主要基于Spring的Environment扩展实现自己的动态配置中心,代码结构如图11-3所示。
图11-3
自定义配置中心的相关说明
在本次案例中,我们并没有完全使用EnvironmentPostProcessor这个扩展点,而是基于SpringFactoriesLoader自定义了一个扩展点,主要目的是让大家知道EnvironmentPostProcessor扩展点的工作原理,以及我们以后自己也可以定义扩展点。
代码实现
以下是所有代码的实现过程,按照下面这个步骤去开发即可完成动态配置中心。
ZookeeperApplicationContextInitializer
ApplicationContextInitializer扩展,它是在ConfigurableApplicationContext通过调用refresh函数来初始化Spring容器之前会进行回调的一个扩展方法,我们可以在这个扩展中实现Environment的扩展。
所以这个类的主要作用就是在ApplicationContext完成refresh之前,扩展Environment,增加外部化配置注入。
1 | java复制代码public class ZookeeperApplicationContextInitializer implements ApplicationContextInitializer<ConfigurableApplicationContext>{ |
创建classpath:/META-INF/spring.factories
1 | properties复制代码org.springframework.context.ApplicationContextInitializer=\ |
PropertySourceLocator
PropertySourceLocator接口支持扩展自定义配置加载到spring Environment中。
1 | java复制代码public interface PropertySourceLocator { |
ZookeeperPropertySourceLocator
ZookeeperPropertySourceLocator用来实现基于Zookeeper属性配置的扩展点,它会访问zookeeper获取远程服务器数据。
1 | java复制代码public class ZookeeperPropertySourceLocator implements PropertySourceLocator{ |
配置扩展点: classpath:/META-INF/spring.factories
1 | properties复制代码com.gupaoedu.example.zookeepercuratordemo.config.PropertySourceLocator=\ |
配置动态变更逻辑
NodeDataCuratorCacheListener
NodeDataCuratorCacheListener用来实现持久化订阅机制,当目标节点数据发生变更时,需要收到变更并且应用。
1 | java复制代码public class NodeDataCuratorCacheListener implements CuratorCacheListenerBuilder.ChangeListener { |
EnvironmentChangeEvent
定义一个环境变量变更事件。
1 | java复制代码public class EnvironmentChangeEvent extends ApplicationEvent { |
ConfigurationPropertiesRebinder
ConfigurationPropertiesRebinder接收事件,并重新绑定@Value注解的数据,使得数据能够动态改变
1 | java复制代码@Component |
ConfigurationPropertiesBeans
ConfigurationPropertiesBeans实现了BeanPostPorocessor接口,该接口我们也叫后置处理器,作用是在Bean对象在实例化和依赖注入完毕后,在显示调用初始化方法的前后添加我们自己的逻辑。注意是Bean实例化完毕后及依赖注入完成后触发的。
我们可以在这个后置处理器的回调方法中,扫描指定注解的bean,收集这些属性,用来触发事件变更。
1 | java复制代码@Component |
RefreshScope
定义注解来实现指定需要动态刷新类的识别。
1 | java复制代码@Target({ ElementType.TYPE, ElementType.METHOD }) |
FieldPair
这个类中主要通过PropertyPlaceholderHelper将字符串里的占位符内容,用我们配置的properties里的替换。
1 | java复制代码public class FieldPair { |
访问测试ConfigController
1 | java复制代码@RefreshScope |
基于自定义PropertySourceLocator扩展
由于在上述代码中,我们创建了一个PropertySourceLocator接口,并且在整个配置加载过程中,我们都是基于PropertySourceLocator扩展点来进行加载的,所以也就是意味着除了上述使用的Zookeeper作为远程配置装载以外,我们还可以通过扩展PropertySourceLocator来实现其他的扩展,具体实现如下
CustomPropertySourceLocator
创建一个MapPropertySource作为Environment的属性源。
1 | java复制代码public class CustomPropertySourceLocator implements PropertySourceLocator{ |
spring.factories
由于CustomPropertySourceLocator是自定义扩展点,所以我们需要在spring.factories文件中定义它的扩展实现,修改如下
1 | properties复制代码com.gupaoedu.example.zookeepercuratordemo.config.PropertySourceLocator=\ |
ConfigController
接下来,我们通过下面的代码进行测试,从结果可以看到,我们自己定义的propertySource被加载到Environment中了。
1 | java复制代码@RefreshScope |
本文转载自: 掘金