zoukankan      html  css  js  c++  java
  • springcloud情操陶冶-springcloud config server(一)

    承接前文springcloud情操陶冶-springcloud context(二),本文将在前文基础上浅析下ConfigServer的工作原理

    前话

    根据前文得知,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}属性都是非必须的。

    小结

    多仓库的外部源加载方式本文是以git为例的,当然springcloud config支持多种方式的加载,有兴趣的读者可自行分析。
    本文主要讲解了ConfigServer如何去读取相应的远程Git文件的逻辑以及读取文件的格式,具体可查阅上文,灵活性还是很强的。既然知道外部资源如何被加载,那么如何被访问也必须得了解下,下文则针对此作详细的分析。

    同时本文主要分析源码,具体的应用其实还是需要查阅官方文档,里面对应用讲的很仔细也很容易入门,就放个小入口,方便以后自己查阅!

  • 相关阅读:
    LeetCode449. 序列化和反序列化二叉搜索树
    LeetCode448. 找到所有数组中消失的数字
    一行代码如何隐藏 Linux 进程?
    C语言这么厉害,它自身又是用什么语言写的?
    了解C语言,是否代表了解C ++的一半?
    C语言小白那些不知道的事儿
    6 条 Git 实用技巧
    干货来袭,收藏方便找到该网站
    零基础小白如何入门Shell,快来看看(收藏)这篇大总结!!
    Java用于嵌入式系统的优点和局限
  • 原文地址:https://www.cnblogs.com/question-sky/p/10304279.html
Copyright © 2011-2022 走看看