进阶Spring微服务架构系列:全局配置

Posted 苏研大云人

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了进阶Spring微服务架构系列:全局配置相关的知识,希望对你有一定的参考价值。


微服务具有良好的伸缩性和扩展性,这是我们比较关注的两个方面。当后台的服务模块越多,随之带来的配置就越多,这样配置集中化管理就变的很有必要,微服务应用一般会有配置的集中管理、不同环境配置的切换、配置的动态更新等需求,下面就来说说Spring-cloud全局配置的事情。


01

全局配置介绍


这里说的全局配置就是配置的集中管理的概念,把原来分散到各个模块的配置,放到配置服务器来管理。说到这里,可能会牵扯出一个问题,所有本地配置都要放到配置服务器上吗?对于这个问题,简单介绍下自己的看法。一般配置按照类型分为业务类的配置,如各种业务的url、模块属性等;功能开关类的配置;服务类配置,如数据库,JMS服务、缓存服务器等。我认为这类配置基本上都是放到配置服务器上,因为不同环境部署时候他们更改的可能性较高。而对于像国际化的属性文件则没有必要放到远程服务器上,一般国际化文件是伴随着编程产生和修改的,这些文件确定后,基本上不会更改,它和代码编程的耦合度非常高。但是有时候也要看自己团队编程约定,毕竟“习惯优于配置”。



下面这张图,简单描述了配置中心与各个客户端关系。客户端可以动态扩展,每天加一个客户端,在服务端加入其对应的配置文件就可以了。


【进阶】Spring微服务架构系列(二):全局配置


当客户端模块启动时候,会通过rest接口去配置中心获取配置信息,并加载到启动的环境信息中去。配置服务器集中管理所有模块的配置。在不同环境配置部署时候,只需修改服务端配置就能实现对应模块配置的修改。下面以Spring-cloud-config为例,介绍下全局配置的实现。

02

Spring-cloud全局配置集成方法


Spring-cloud-config包含服务端与客户端,并与Spring-boot无缝集成。网上大多数介绍都是以git仓库为例来介绍配置中心搭建的,这里我就换个方式,以读取本地磁盘配置的方式来介绍服务端的搭建。

首先,我们创建一个maven工程,并在pom文件中加入以下配置,来加载依赖的jar包。

【进阶】Spring微服务架构系列(二):全局配置



然后,创建启动类,并加入启动配置服务注解。

【进阶】Spring微服务架构系列(二):全局配置

最后一步,就是在配置文件加入读取本地文件的配置。这样,一个简单的配置服务器就配置成功了。

【进阶】Spring微服务架构系列(二):全局配置

到这里,我们就完成了配置服务器的搭建。我们启动工程,路径/var/lib/config的配置文件都会被读取。属性"pring.profiles.active=native"作用是激活读取本地文件的配置,如果没有这一句,本地文件是不会被读取的。例如在配置文件路径下,有个配置文件名称是“epic-common.properties”,当服务启动后,可以通过浏览器中访问“http://ip:port/epic/common”来访问到配置文件内容。另外你可以在/var/lib/config这个目录下创建不同的目录来区别开发环境(dev)、测试环境(test)等。浏览器访问时,只需路径变为 "http://localhost:port/epic/common/dev"就可以了。通过rest接口读取配置文件内容,网上相关的文章实在很多,感兴趣的可以去看看。



通过以上步骤,我们简单完成了服务端端搭建。客户端做一下修改就可以使用全局配置功能了。首先,在pom.xml.加入一下引用:

【进阶】Spring微服务架构系列(二):全局配置

然后,在资源目录下加入bootstrap.yml文件,加入对应配置。比如想要读取远程配置文件名称是"epic-common"的属性文件的配置如下,也可以同时读取多个文件等配置信息。

【进阶】Spring微服务架构系列(二):全局配置

03

全局配置服务端代码简析


我们首先从服务端入手,介绍下Spring加载配置的基本思路。服务端Spring环境的配置类是EnvironmentRepositoryConfiguration,它在org.springframework.cloud.config.server.config这个pacakge下。介绍这个类之前,先简单了解下Spring的“@Profile”注解,它的作用是标明当前的运行环境。只有当对应的Profile被激活时候,Spring才会将Profile所对应的Bean注册到Spring容器中。配置服务器的搭建过程中我们通过了"spring.profiles.active=native"来激活值为''native"的profile。

在EnvironmentRepositoryConfiguration中,源码中有这么一个配置:

【进阶】Spring微服务架构系列(二):全局配置



这个DefaultRepositoryConfiguration的默认配置,用来加载读取git仓库中配置文件的bean。配置类上的注解@ConditionalOnMissingBean(EnvironmentRepository.class)作用是当环境中存在实现了EnvironmentRepository这个接口的Bean时候就不会再去创建DefaultRepositoryConfiguration这个配置Bean。当这个配置Bean被创建,里面的MultipleJGitEnvironmentRepository对应的Bean就会被加载到Spring容器中去。

我们接着往下看EnvironmentRepositoryConfiguration的代码,就会发现下面这个配置:

【进阶】Spring微服务架构系列(二):全局配置



这个配置在"native"环境就会被激活,里面的NativeEnvironmentRepository实现了EnvironmentRepository这个接口,所以当这个配置类起作用时候,默认的DefaultRepositoryConfiguration的Bean就不会被Spring容器创建。

另外,EnvironmentRepositoryConfiguration中还有支持svn的配置。不妨我们稍微发散下,假如要以数据库中的表来存放配置信息的话,就可以定义一个实现EnvironmentRepository接口的类,并在这个类里面完成数据库配置属性的加载,然后在工程中加入自己的DatabaseRepositoryConfiguration,这样的就可以完成读取数据库配置信息的功能了。

04

客户端配置读取简析


我们首先了解下在没有全局配置情况下,Spring-boot读取配置文件的方式。Spring-boot启动时,会加载一些默认的监听器,其中有一个监听器是ConfigFileApplicationListener,这个监听器的作用就是读取工程中的配置信息。我们简单梳理下启动流程,首先启动类调用SpringApplication的run方法,并在run方法中调用了SpringApplication的构造方法,然后构造方法中初始化了Spring-boot启动需要的监听器;这个ConfigFileApplicationListener监听了SpringApplication启动事件,监听代码如下:

【进阶】Spring微服务架构系列(二):全局配置

当启动时候,触发监听器执行任务,执行过程中调用了postProcessEnvironment方法,这个方法中的“addPropertySources(environment, application.getResourceLoader())”这句代码,便是添加属性到上下文环境中去。我们顺着这句代码走下去,就会看到具体实现是通过内部类Loader实现的。



这里简要说一下Loader类中加载配置文件代码逻辑。首先创建PropertySourcesLoader对象,并通用PropertySourcesLoader对象在目录classpath:/(类加载目录)、classpath:/config/(类加载目录下的config目录)、file:./(当前目录)、file:./config/(当前目录下的config目录)中,查找名称为application的属性文件(后缀名可以是properties、xml、yml、yaml)。通过上面步骤,Spring就会读取到所有配置信息,接下来通过addConfigurationProperties()方法将属性放入到环境变量的propertySources中去。源码如下:

【进阶】Spring微服务架构系列(二):全局配置



如果加入全局配置后,远程配置文件是如何放入到环境变量的propertySources中去呢?读取本地配置的代码不变,接着后面在SpringApplication.applyInitializers()方法中,会调用PropertySourceBootstrapConfiguration.initialize()的方法,PropertySourceBootstrapConfiguration是一个重要的类,它实现了ApplicationContextInitializer接口,该接口会在应用上下文刷新之前被调用,其对应的initialize()方法实现如下:


【进阶】Spring微服务架构系列(二):全局配置

上面代码简要逻辑是,根据默认的 AnnotationAwareOrderComparator 排序规则对propertySourceLocators数组进行排序,这个数组中存放的就是全局配置的访问信息,如URL,配置名称等。接着代码中遍历propertySourceLocators这个对象,循环中调用PropertySourceLocator.locate方法将source添加到PropertySource的链表中。这里我们可以看一下PropertySourceLocator接口的实现类ConfigServicePropertySourceLocator源码就会知道——在其locate方法中,使用RestTemplate读取了远程配置中心的配置信息,然后通过代码,"insertPropertySources(propertySources, composite);"将远程取得的配置信息放入到环境变量的propertySources中。这样,不管是本地配置信息,还是远程配置信息都已经加载完成。



另外补充一点,在此种情况下,propertySources中配置置文件读取是按照顺序进行的,当对应的属性读取到值之后,下面的文件就不会被遍历。表现出来的现象就是Spring-boot是按照一下顺序读取配置文件的。

1. 远程配置文件

2. 当前目录的config子目录

3. 当前目录

4. classpath下的config子目录

5. classpath目录

如果在实际开发过程中,调试需要改变文件的优先级,可以通过实现ApplicationContextInitializer<ConfigurableApplicationContext>接口,来改变读取文件的顺序,具体实现代码这里就不介绍了。

05

Spring-cloud配置的更新与动态加载


Spring-cloud提供了一种动态更新机制,允许在不重启应用的就可以动态感知配置文件的变化并应用新配置。其实现是通过消息队列来实现的。对应的模块是Spring-cloud-bus。

【进阶】Spring微服务架构系列(二):全局配置


配置中心感知到配置变化,向Bus投递消息;Bus服务广播消息;各个服务模块收到消息后,主动向配置中心拉取新配置并应用。各个服务模块可以感知git提交自动发送广播,或者手动触发广播消息的发送。另外结合注册服务器,可以对部署的某一个模块局部刷新,这一功能在部署后项目维护上提供了很多便利。


以上是关于进阶Spring微服务架构系列:全局配置的主要内容,如果未能解决你的问题,请参考以下文章

一文讲清Spring Cloud 微服务架构的五脏六腑!

最新版Spring Cloud Alibaba微服务架构-Openfeign服务调用篇

最新版Spring Cloud Alibaba微服务架构-Openfeign服务调用篇

微服务架构系列之–最全配置中心对比(面试随便装)

Spring Cloud微服务架构—服务注册与发现

Spring Cloud微服务架构—服务注册与发现