二十四.SpringCloudConfig源码-配置拉取流程

简介: 这篇文章是接上一篇的,因为文章太长看起来太累,所以就分了一下

前言

这篇文章是接上一篇的,因为文章太长看起来太累,所以就分了一下

EnvironmentRepository.findOne 查找配置

上回说到 EnvironmentController 控制器 ,重点关注 labelled 方法, 我打了一个断点跟了一下,代码会走到了 EnvironmentEncryptorEnvironmentRepository#findOne方法中,源码如下:

@Override
public Environment findOne(String name, String profiles, String label) {
   
    //调用了SearchPathCompositeEnvironmentRepository#findOne
    Environment environment = this.delegate.findOne(name, profiles, label);
    if (this.environmentEncryptor != null) {
   
        //解密
        environment = this.environmentEncryptor.decrypt(environment);
    }
    if (!this.overrides.isEmpty()) {
   
        environment.addFirst(new PropertySource("overrides", this.overrides));
    }
    return environment;
}

通过断点看到,在EnvironmentEncryptorEnvironmentRepository#findOne方法中,调用了SearchPathCompositeEnvironmentRepository#findOne方法,SearchPathCompositeEnvironmentRepository 是一个可以通过SearchPathLocator从文件系统中加载配置文件的EnvironmentRepository

SearchPathCompositeEnvironmentRepository并没有findOne方法,它使用的父类的方法CompositeEnvironmentRepository#findOne,最终得到一个Environment 对象。然后通过 EnvironmentEncryptor 进行解密。

进行往下走流程,代码来到 CompositeEnvironmentRepository#findOne

public class CompositeEnvironmentRepository implements EnvironmentRepository {
   
    //仓库列表
    protected List<EnvironmentRepository> environmentRepositories;
    @Override
    public Environment findOne(String application, String profile, String label) {
   
        //Environment 是对配置文件名,环境,分支等封装对象
        Environment env = new Environment(application, new String[]{
   profile}, label, null, null);
        //是不是只配置了一个仓库
        if(environmentRepositories.size() == 1) {
   
            //这里调用的是MultipleJGitEnvironmentRepository#findOne
            Environment envRepo = environmentRepositories.get(0).findOne(application, profile, label);
            //把加载到的配置文件的内容设置给Environment 
            env.addAll(envRepo.getPropertySources());
            //版本号
            env.setVersion(envRepo.getVersion());
            //状态
            env.setState(envRepo.getState());
        } else {
   
            //如果配置了多个仓库,循环通过repo.findOne去下载配置
            for (EnvironmentRepository repo : environmentRepositories) {
   
                env.addAll(repo.findOne(application, profile, label).getPropertySources());
            }
        }
        return env;
}

这里把配置文件名,环境名,分支名封装成Environment 对象,同时该对象也用来接收结果。然后判断了一下是否配置了多个仓库,多个仓库就循环调用findOne方法加载配置,我们这里只配置了一个仓库,走的是MultipleJGitEnvironmentRepository的findOne方法,然后把结果设置给Environment 并返回Environment。

注意:env.addAll(envRepo.getPropertySources()); 这行代码,PropertySources就是加载到的配置文件的内容了。

继续往后面走,代码来到 MultipleJGitEnvironmentRepository#findOne 方法中

@Override
    public Environment findOne(String application, String profile, String label) {
   

        ...省略...
        //这个getRepository方法中对url中的类似{application}的占位符做了一些替换
        JGitEnvironmentRepository candidate = getRepository(this, application, profile,
                label);
        if (label == null) {
   
            //如果label是null,默认使用master作为分支
            label = candidate.getDefaultLabel();
        }
        if (candidate == this) {
   
            //默认走这
            return super.findOne(application, profile, label);
        }
        return candidate.findOne(application, profile, label);
    }

该方法中对URL中的占位符如:{application},{label}等进行替换,然后判断如果label是空的,就使用master作为默认label。最后调用super.findOne方法,此时代码来到其父类 AbstractScmEnvironmentRepository#findOne 方法

@Override
public synchronized Environment findOne(String application, String profile, String label) {
   
    //加载本机的环境存储库
    NativeEnvironmentRepository delegate = new NativeEnvironmentRepository(getEnvironment(),
            new NativeEnvironmentProperties());
    //加载Locations ,Locations是对本地配置文件的封装
    Locations locations = getLocations(application, profile, label);
    //locations.getLocations()得到本地配置文件路径
    delegate.setSearchLocations(locations.getLocations());
    //调用 NativeEnvironmentRepository #findOne方法加载配置
    Environment result = delegate.findOne(application, profile, "");
    //把版本号和label设置给result
    result.setVersion(locations.getVersion());
    result.setLabel(label);
    //执行本地仓库的清理工作
    return this.cleaner.clean(result, getWorkingDirectory().toURI().toString(),
            getUri());
}

该方法做了这么几件事情:

  1. 把ConfigurableEnvironment交给一个NativeEnvironmentRepository对象,ConfigurableEnvironment中有当前配置中心微服务的原始配置。
  2. 调用 getLocations 方法得到Locations ,它封装了配置文件的 application,profile,label,version ,和本地存储远程拉取下来的配置文件的位置。如:file:/C:/Users/whale/AppData/Local/Temp/config-repo-8104345609176998816/
  3. 把配置文件的地址交给NativeEnvironmentRepository ,然后调用其findOne方法
  4. 最后执行clean清理

getLocations 加载本地配置

我们先看一下 getLocations 方法是怎么加载本地配置文件的,代码来到JGitEnvironmentRepository#getLocations

@Override
public synchronized Locations getLocations(String application, String profile,
        String label) {
   
    if (label == null) {
   
    //默认使用master分支
        label = this.defaultLabel;
    }
    //刷新配置,这个代码回去远程拉取最新的配置文件
    String version = refresh(label);
    //调用 getSearchLocations 处理一下本地配置文件地址,把结果封装成Locations
    return new Locations(application, profile, label, version,
            getSearchLocations(getWorkingDirectory(), application, profile, label));
}

这里调用了 refresh方法下载配置文件,然后调用 getSearchLocations 找到本地下载的配置文件地址,封装成Locations返回。

重点看一下 refresh方法,代码来到JGitEnvironmentRepository#refresh方法中


    /**准备工作目录
     * Get the working directory ready.
     */
    public String refresh(String label) {
   
        Git git = null;
        try {
   
            //【1】创建Git客户端
            git = createGitClient();
            //【2】判断是否要从远程拉取配置文件
            if (shouldPull(git)) {
   
            //【3】执行配置文件拉取动作
                FetchResult fetchStatus = fetch(git, label);
                if (deleteUntrackedBranches && fetchStatus != null) {
   
                    deleteUntrackedLocalBranches(fetchStatus.getTrackingRefUpdates(), git);
                }
                // checkout after fetch so we can get any new branches, tags, ect.
                //【4】执行checkout操作,切到master分支
                checkout(git, label);
                //判断仓库的所有分支是否包含当前分支
                if (isBranch(git, label)) {
   
                    // merge results from fetch
                    //【5】执行merge操作
                    merge(git, label);
                    if (!isClean(git, label)) {
   
                        logger.warn("The local repository is dirty or ahead of origin. Resetting"
                                + " it to origin/" + label + ".");
                        resetHard(git, label, LOCAL_BRANCH_REF_PREFIX + label);
                    }
                }
            }
            else {
   
                // nothing to update so just checkout
                checkout(git, label);
            }
            //【6】始终返回当前HEAD作为版本 , 把版本号返回
            // always return what is currently HEAD as the version
            return git.getRepository().findRef("HEAD").getObjectId().getName();
        }
        ...省略...
    }
    //创建Git客户端
    private Git createGitClient() throws IOException, GitAPIException {
   
        //工作目录加锁
        File lock = new File(getWorkingDirectory(), ".git/index.lock");
        //锁是否存在
        if (lock.exists()) {
   
            // The only way this can happen is if another JVM (e.g. one that
            // crashed earlier) created the lock. We can attempt to recover by
            // wiping the slate clean.
            logger.info("Deleting stale JGit lock file at " + lock);
            //删除锁
            lock.delete();
        }
        //工作中的配置文件是否创造
        if (new File(getWorkingDirectory(), ".git").exists()) {
   
            //打开Git仓库
            return openGitRepository();
        }
        else {
   
        //拷贝Git仓库
            return copyRepository();
        }
    }

JGitEnvironmentRepository#refresh方法挺复杂的,简单点锁就是根据URL把Git仓库中的配置文件fetch到本地,然后进行checkout,merge等等。然后把本地配置文件的地址。

代码还要回到 .AbstractScmEnvironmentRepository#findOne ,刚才跟的是getLocations方法

@Override
    public synchronized Environment findOne(String application, String profile, String label) {
   
        NativeEnvironmentRepository delegate = new NativeEnvironmentRepository(getEnvironment(),
                new NativeEnvironmentProperties());
        //查找配置的地址
        Locations locations = getLocations(application, profile, label);
        delegate.setSearchLocations(locations.getLocations());
        //把locaitons变成 Environment对象
        Environment result = delegate.findOne(application, profile, "");
        result.setVersion(locations.getVersion());
        result.setLabel(label);
        return this.cleaner.clean(result, getWorkingDirectory().toURI().toString(),
                getUri());
    }

封装Environment

现在需要跟一下 delegate.findOne方法了,这里调用的是NativeEnvironmentRepository#findOne ,它主要是把配置封装成Environment 对象。

@Override
    public Environment findOne(String config, String profile, String label) {
   
        SpringApplicationBuilder builder = new SpringApplicationBuilder(
                PropertyPlaceholderAutoConfiguration.class);
        //本机环境
        ConfigurableEnvironment environment = getEnvironment(profile);
        builder.environment(environment);
        builder.web(WebApplicationType.NONE).bannerMode(Mode.OFF);
        if (!logger.isDebugEnabled()) {
   
            // Make the mini-application startup less verbose
            builder.logStartupInfo(false);
        }
        //args中包括 application , profile , label, 配置文件地址 
        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 {
   
        //clean方法会返回一个封装好配置项的Environment 
            return clean(new PassthruEnvironmentRepository(environment).findOne(config,
                    profile, label));
        }
        finally {
   
            context.close();
        }
    }

文章结束,喜欢的话给个好评吧!!!

相关文章
|
25天前
|
缓存 监控 Java
Java线程池提交任务流程底层源码与源码解析
【11月更文挑战第30天】嘿,各位技术爱好者们,今天咱们来聊聊Java线程池提交任务的底层源码与源码解析。作为一个资深的Java开发者,我相信你一定对线程池并不陌生。线程池作为并发编程中的一大利器,其重要性不言而喻。今天,我将以对话的方式,带你一步步深入线程池的奥秘,从概述到功能点,再到背景和业务点,最后到底层原理和示例,让你对线程池有一个全新的认识。
53 12
|
7月前
|
Java Maven
分模块开发的意义及开发步骤
分模块开发的意义及开发步骤
67 3
分模块开发的意义及开发步骤
|
7月前
|
缓存 Kubernetes 安全
小而美:两步完成从源码到应用的极简交付
本文将主要介绍,如何通过 SAE 快速实现项目从源码到应用的交付与上线。
51777 1
|
SQL JSON 机器人
pytest+yaml设计接口自动化框架过程记录(一步一步记录如何设计,完结撒花),源码提供,视频教程
pytest+yaml设计接口自动化框架过程记录(一步一步记录如何设计,完结撒花),源码提供,视频教程
|
Kubernetes 测试技术 Go
分享:一文搞清楚应用发布到k8s集群的基本流程
分享:一文搞清楚应用发布到k8s集群的基本流程
385 0
|
JSON 前端开发 Java
Bug实录 | 第一篇 :重写WebMvcConfigurationSupport后SpringBoot自动配置失效
Bug实录 | 第一篇 :重写WebMvcConfigurationSupport后SpringBoot自动配置失效
|
Prometheus Kubernetes Cloud Native
Flagger(应用自动发布)介绍和原理剖析
## 简介 [Flagger](https://github.com/weaveworks/flagger)是一个能使运行在k8s体系上的应用发布流程全自动(无人参与)的工具, 它能减少发布的人为关注时间, 并且在发布过程中能自动识别一些风险(例如:RT,成功率,自定义metrics)并回滚. ## 主要特性 ![features](https://intranetproxy.ali
4521 0
|
Oracle IDE Java
最详细的Android开发环境配置经验分享(包含配置过程中可能出现的问题及解决办法。繁琐的配置步骤是否是你头疼呢,详细配置步骤你值得拥有!)
最详细的Android开发环境配置经验分享(包含配置过程中可能出现的问题及解决办法。繁琐的配置步骤是否是你头疼呢,详细配置步骤你值得拥有!)
433 0
最详细的Android开发环境配置经验分享(包含配置过程中可能出现的问题及解决办法。繁琐的配置步骤是否是你头疼呢,详细配置步骤你值得拥有!)
|
消息中间件 Java RocketMQ
拉取信息的流程小结|学习笔记
快速学习拉取信息的流程小结
拉取信息的流程小结|学习笔记
|
中间件 测试技术
NestJS 7.x 折腾记: (1) 项目初始化及常规提交门禁加入
系列常规操作,没兴趣的可以跳过这篇水文. 写过Angular 2+的小伙伴会有一种天然的熟悉感. 因为Nest基本就是同一个思想模式搞得~~
236 0

热门文章

最新文章