springcloud ConfigServer的工作原理

Easter79
• 阅读 555

前话

根据前文得知,bootstrapContext引入了PropertySourceLocator接口供外部源加载配置,但作用是应用于子级ApplicationContext的环境变量Environment上,并不做更新维护操作。

具体的加载与维护更新外部源的配置信息,还是得有ConfigServer来完成,这也是本文分析的重点。

监听器

在这之前,笔者先查看此板块关联的监听器ConfigServerBootstrapApplicationListener,因为其比前文分析的_BootstrapApplicationListener_监听器优先级还高,不过内部代码很简单,笔者直接查看其复写的方法

    @Override
    public void onApplicationEvent(ApplicationEnvironmentPreparedEvent event) { ConfigurableEnvironment environment = event.getEnvironment(); if (!environment.resolvePlaceholders("${spring.cloud.config.enabled:false}") .equalsIgnoreCase("true")) { if (!environment.getPropertySources().contains(this.propertySource.getName())) { environment.getPropertySources().addLast(this.propertySource); } } }

代码意思很简单,针对环境变量中_spring.cloud.config.enabled_属性如果值不为_true_则设置为_false_。根据官方上的代码注释来看,是用于屏蔽HTTP方式的访问,默认是开启屏蔽功能的。也就是屏蔽了ConfigServer暴露Restful方式的接口访问,其中该属性可通过System系统变量或者SpringApplicationBuilder类来进行设置,具体读者可查阅其官方注释

这个影响小,我们直接去查看其如何去加载外部源的

BootstrapContext关联类

优先分析与bootstrapContext相关的类,通过查看其板块下的_spring.factories_文件对应的BootstrapConfiguration键值

# Bootstrap components
org.springframework.cloud.bootstrap.BootstrapConfiguration=\
org.springframework.cloud.config.server.bootstrap.ConfigServerBootstrapConfiguration,\
org.springframework.cloud.config.server.config.EncryptionAutoConfiguration

笔者挑选ConfigServerBootstrapConfiguration类作为主要的分析源头,内部的源码比较简单,笔者则全部放出来

// 系统变量或者bootstrap.properties文件指定了spring.cloud.config.server.bootstrap属性则生效
@Configuration
@ConditionalOnProperty("spring.cloud.config.server.bootstrap")
public class ConfigServerBootstrapConfiguration { @EnableConfigurationProperties(ConfigServerProperties.class) @Import({ EnvironmentRepositoryConfiguration.class }) protected static class LocalPropertySourceLocatorConfiguration { @Autowired private EnvironmentRepository repository; @Autowired private ConfigClientProperties client; @Autowired private ConfigServerProperties server; // 加载外部源入口 @Bean public EnvironmentRepositoryPropertySourceLocator environmentRepositoryPropertySourceLocator() { return new EnvironmentRepositoryPropertySourceLocator(this.repository, this.client.getName(), this.client.getProfile(), getDefaultLabel()); } private String getDefaultLabel() { if (StringUtils.hasText(this.client.getLabel())) { return this.client.getLabel(); } else if (StringUtils.hasText(this.server.getDefaultLabel())) { return this.server.getDefaultLabel(); } return null; } } }

根据当前环境下是否存在_spring.cloud.config.server.bootstrap_属性来决定是否通过Git/SVN/Vault等方式(下文将提及)加载外部源至子级的ConfigurableEnvironment对象中,默认不开启,需要用户配置。

具体通过什么方式获取外部资源则交由EnvironmentRepository接口去实现,我们先看下此接口的方法

public interface EnvironmentRepository {

    // 内部就一个方法,通过参数指定找寻对应的环境对象 Environment findOne(String application, String profile, String label); }

看来其支持多仓库源的配置,但这里注意一下此处的_Environment_回参是springcloud config client板块中的类,应该是对我们常见的环境变量作些过滤的作用。

EnvironmentRepositoryConfiguration

除了上述的方式引入此多环境仓库的配置类,ConfigServer对应的ConfigServerAutoConfiguration默认也会引入。废话少说,首先看下头部

@Configuration
@EnableConfigurationProperties({ SvnKitEnvironmentProperties.class, CredhubEnvironmentProperties.class,
        JdbcEnvironmentProperties.class, NativeEnvironmentProperties.class, VaultEnvironmentProperties.class })
@Import({ CompositeRepositoryConfiguration.class, JdbcRepositoryConfiguration.class, VaultRepositoryConfiguration.class,
        CredhubConfiguration.class, CredhubRepositoryConfiguration.class, SvnRepositoryConfiguration.class,
        NativeRepositoryConfiguration.class, GitRepositoryConfiguration.class, DefaultRepositoryConfiguration.class })
public class EnvironmentRepositoryConfiguration { }

嗯,看起来很多,其实也就是针对不同源的资源进行相应的配置,比如常见的SVN/Jdbc/Git/Vault等方式。针对不同源,springcloud允许用户配置_spring.profile.active_属性来选择相应的源,即使不指定,springcloud也默认以Git方式获取仓库。本文以springcloud默认支持的Git方式作为分析的入口

GitRepositoryConfiguration

git方式的资源获取是通过配置_GitRepositoryConfiguration_类来实现的,笔者看下其代码

@Configuration
@Profile("git")
class GitRepositoryConfiguration extends DefaultRepositoryConfiguration { }

直接去观察其继承的_DefaultRepositoryConfiguration_类,内部源码也很简单,顺便把其关联的一些bean也一同放上来,方便我们更清楚的了解

    // 多Git环境仓库属性配置,以spring.cloud.config.server.git作为开头
    @Bean
    @ConditionalOnMissingBean(search = SearchStrategy.CURRENT)
    public MultipleJGitEnvironmentProperties multipleJGitEnvironmentProperties() { return new MultipleJGitEnvironmentProperties(); } @Configuration @ConditionalOnClass(TransportConfigCallback.class) static class JGitFactoryConfig { // 多Git环境仓库的工厂类 @Bean public MultipleJGitEnvironmentRepositoryFactory gitEnvironmentRepositoryFactory( ConfigurableEnvironment environment, ConfigServerProperties server, Optional<ConfigurableHttpConnectionFactory> jgitHttpConnectionFactory, Optional<TransportConfigCallback> customTransportConfigCallback) { return new MultipleJGitEnvironmentRepositoryFactory(environment, server, jgitHttpConnectionFactory, customTransportConfigCallback); } } @Configuration @ConditionalOnClass({ HttpClient.class, TransportConfigCallback.class }) static class JGitHttpClientConfig { // HTTP连接工厂类 @Bean public ConfigurableHttpConnectionFactory httpClientConnectionFactory() { return new HttpClientConfigurableHttpConnectionFactory(); } } @Configuration @ConditionalOnMissingBean(value = EnvironmentRepository.class, search = SearchStrategy.CURRENT) class DefaultRepositoryConfiguration { .... .... @Bean public MultipleJGitEnvironmentRepository defaultEnvironmentRepository( MultipleJGitEnvironmentRepositoryFactory gitEnvironmentRepositoryFactory, MultipleJGitEnvironmentProperties environmentProperties) throws Exception { return gitEnvironmentRepositoryFactory.build(environmentProperties); } }

这里注册的MultipleJGitEnvironmentRepository对象便是_EnvironmentRepository_接口的实现类,由其统一管理多Git仓库的资源。在分析此类之前,先对上述的代码作下分步骤的分析以免产生糊涂


1.多Git仓库属性配置MultipleJGitEnvironmentProperties,也就是配置Git仓库的地址以及访问方式等等。挑选比较重要的属性用于归纳(多仓库应用)

假设远程仓库地址为git@github.com:jtjsir/config_demo.git

spring.cloud.config.server.git.repos.A1.pattern=config*     #A1仓库的匹配规则(匹配源{application}/{profile}),默认为下一点的name
spring.cloud.config.server.git.repos.A1.name=config_demo        #A1仓库的别名
spring.cloud.config.server.git.repos.A1.uri=git@github.com:jtjsir/config_demo.git #远程git仓库地址
spring.cloud.config.server.git.repos.A1.username=nancoasky@gmail.com    #git帐号
spring.cloud.config.server.git.repos.A1.password=nanco123   #git密码
spring.cloud.config.server.git.repos.A1.passphrase=     #ssh密码短语,默认为空
spring.cloud.config.server.git.repos.A1.basedir=/data/demo/cloud    #本地保存路径
spring.cloud.config.server.git.repos.A1.defaultLabel=master #标签,类似git的分支概念

具体的用户可查看_MultipleJGitEnvironmentProperties_类去详细的查看各个属性的含义,同时也可以了解SSH方式的校验


2.Http连接工厂类HttpClientConfigurableHttpConnectionFactory,主要是支持http/https的Git访问方式。具体就不讲解了,读者可自行分析

MultipleJGitEnvironmentRepository

顾名思义,其实就是JGitEnvironmentRepository类的集合类,我们只需要关注其复写的_findOne()_方法,附上真正去查找相应配置资源的**AbstractScmEnvironmentRepository#findOne()**方法

    @Override
    public synchronized Environment findOne(String application, String profile, String label) { // 通过native方式去加载,也就是读取远程Git仓库的本地copy NativeEnvironmentRepository delegate = new NativeEnvironmentRepository(getEnvironment(), new NativeEnvironmentProperties()); // 1.获取本地git仓库的查找路径 Locations locations = getLocations(application, profile, label); delegate.setSearchLocations(locations.getLocations()); // 2.获取属性集合 Environment result = delegate.findOne(application, profile, ""); result.setVersion(locations.getVersion()); result.setLabel(label); // 过滤下 return this.cleaner.clean(result, getWorkingDirectory().toURI().toString(), getUri()); }

笔者分析上述标注的两点,分步骤来


1.获取本地git仓库的查找路径,对应的是**JGitEnvironmentRepository#getLocations()**方法

    @Override
    public synchronized Locations getLocations(String application, String profile, String label) { // label代表git仓库的分支,默认为master if (label == null) { label = this.defaultLabel; } // 刷新本地git仓库,蕴含了拉取远程仓库、更新的操作。使用到了uri属性 String version = refresh(label); // 使用到了basedir和searchPaths属性 return new Locations(application, profile, label, version, getSearchLocations(getWorkingDirectory(), application, profile, label)); }

上述的搜寻路径格式如**{basedir}/{searchPaths:/}**。其中_searchPaths_的组合方式是{application}、{profile}、{label}的随意拼装,有很大的灵活性。比如

{basedir}/{application}/{profile}/{label:master}/
{basedir}/{application}-{profile}/{label:master}/
{basedir}/{label:master}/{application}/{profile}/

{basedir}/config_demo

其中{application}、{profile}、{label}属性都是非必须的。

备注
如果用户有多层目录的要求,则只需要通过(_)来代替"/"即可。
比如_searchPaths={application}_,如果有二级目录则使用application(_)profile即可


2.获取属性集合,具体的如何去解析获取相应的配置信息且看_NativeEnvironmentRepository#findOne()_方法

    // 此时的label为空字符串
    @Override
    public Environment findOne(String config, String profile, String label) { // 专门解析${}符号 SpringApplicationBuilder builder = new SpringApplicationBuilder( PropertyPlaceholderAutoConfiguration.class); // 设置spring.profiles.active=profile ConfigurableEnvironment environment = getEnvironment(profile); builder.environment(environment); builder.web(WebApplicationType.NONE).bannerMode(Mode.OFF); /** 设置spring.config.name=config,application ** 设置spring.config.location={basedir}/{searchPaths:/} ** */ String[] args = getArgs(config, profile, label); // Explicitly set the listeners (to exclude logging listener which would change // log levels in the caller) builder.application() .setListeners(Arrays.asList(new ConfigFileApplicationListener())); ConfigurableApplicationContext context = builder.run(args); environment.getPropertySources().remove("profiles"); try { // 过滤系统内部的通用变量并缩减source对应的key return clean(new PassthruEnvironmentRepository(environment).findOne(config, profile, label)); } finally { context.close(); } }

其实很简单就是跟我们平常springboot启动时一样,读取相应的配置文件**(此处只支持yml、properties、yaml方式)**,读取的格式例子如下

{basedir}/{searchPaths:/}application.properties
{basedir}/{searchPaths:/}application.yml
{basedir}/{searchPaths:/}{application}.properties
{basedir}/{searchPaths:/}{application}.yml
{basedir}/{searchPaths:/}{application}-{profile}.yml
{basedir}/{searchPaths:/}{application}-{profile}.properties
---
{basedir}/{application}/{profile}/{label:master}/application.[properties|yml]
{basedir}/{application}-{profile}/{label:master}/{application}.[properties|yml]
{basedir}/{label:master}/{application}/{profile}/{application}-{profile}.[properties|yml]
---
{basedir}/config_demo/{application}-{profile}.[properties|yml]
{basedir}/config_demo/application.[properties|yml]

其中{application}、{profile}、{label}属性都是非必须的。

点赞
收藏
评论区
推荐文章
blmius blmius
3年前
MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1
文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s
皕杰报表之UUID
​在我们用皕杰报表工具设计填报报表时,如何在新增行里自动增加id呢?能新增整数排序id吗?目前可以在新增行里自动增加id,但只能用uuid函数增加UUID编码,不能新增整数排序id。uuid函数说明:获取一个UUID,可以在填报表中用来创建数据ID语法:uuid()或uuid(sep)参数说明:sep布尔值,生成的uuid中是否包含分隔符'',缺省为
待兔 待兔
3个月前
手写Java HashMap源码
HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程22
Jacquelyn38 Jacquelyn38
3年前
2020年前端实用代码段,为你的工作保驾护航
有空的时候,自己总结了几个代码段,在开发中也经常使用,谢谢。1、使用解构获取json数据let jsonData  id: 1,status: "OK",data: 'a', 'b';let  id, status, data: number   jsonData;console.log(id, status, number )
Stella981 Stella981
3年前
KVM调整cpu和内存
一.修改kvm虚拟机的配置1、virsheditcentos7找到“memory”和“vcpu”标签,将<namecentos7</name<uuid2220a6d1a36a4fbb8523e078b3dfe795</uuid
Wesley13 Wesley13
3年前
mysql设置时区
mysql设置时区mysql\_query("SETtime\_zone'8:00'")ordie('时区设置失败,请联系管理员!');中国在东8区所以加8方法二:selectcount(user\_id)asdevice,CONVERT\_TZ(FROM\_UNIXTIME(reg\_time),'08:00','0
Wesley13 Wesley13
3年前
00:Java简单了解
浅谈Java之概述Java是SUN(StanfordUniversityNetwork),斯坦福大学网络公司)1995年推出的一门高级编程语言。Java是一种面向Internet的编程语言。随着Java技术在web方面的不断成熟,已经成为Web应用程序的首选开发语言。Java是简单易学,完全面向对象,安全可靠,与平台无关的编程语言。
Stella981 Stella981
3年前
Django中Admin中的一些参数配置
设置在列表中显示的字段,id为django模型默认的主键list_display('id','name','sex','profession','email','qq','phone','status','create_time')设置在列表可编辑字段list_editable
Wesley13 Wesley13
3年前
MySQL部分从库上面因为大量的临时表tmp_table造成慢查询
背景描述Time:20190124T00:08:14.70572408:00User@Host:@Id:Schema:sentrymetaLast_errno:0Killed:0Query_time:0.315758Lock_
Python进阶者 Python进阶者
9个月前
Excel中这日期老是出来00:00:00,怎么用Pandas把这个去除
大家好,我是皮皮。一、前言前几天在Python白银交流群【上海新年人】问了一个Pandas数据筛选的问题。问题如下:这日期老是出来00:00:00,怎么把这个去除。二、实现过程后来【论草莓如何成为冻干莓】给了一个思路和代码如下:pd.toexcel之前把这
Easter79
Easter79
Lv1
今生可爱与温柔,每一样都不能少。
文章
2.8k
粉丝
5
获赞
1.2k