二十四.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();
        }
    }

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

相关文章
|
存储 运维 安全
【运维知识高级篇】一篇文章带你搞懂GitHub基础操作!(注册用户+配置ssh-key+创建项目+创建存储库+拉取代码到本地+推送新代码到Github)
【运维知识高级篇】一篇文章带你搞懂GitHub基础操作!(注册用户+配置ssh-key+创建项目+创建存储库+拉取代码到本地+推送新代码到Github)
343 0
|
Kubernetes Cloud Native jenkins
下篇:使用jenkins发布go项目到k8s,接上篇的手工体验改造为自动化发布
下篇:使用jenkins发布go项目到k8s,接上篇的手工体验改造为自动化发布
604 1
|
3月前
|
jenkins 持续交付 API
enkins学习笔记之十一:优化Gitlab提交流水线
enkins学习笔记之十一:优化Gitlab提交流水线
enkins学习笔记之十一:优化Gitlab提交流水线
|
3月前
|
JSON Android开发 数据格式
Android项目架构设计问题之在远端动态配置中添加相应配置如何解决
Android项目架构设计问题之在远端动态配置中添加相应配置如何解决
28 0
|
4月前
|
监控 JavaScript Serverless
函数计算产品使用问题之如何部署一个未构建的Vue项目,并实现从Gitee仓库自动同步更新
函数计算产品作为一种事件驱动的全托管计算服务,让用户能够专注于业务逻辑的编写,而无需关心底层服务器的管理与运维。你可以有效地利用函数计算产品来支撑各类应用场景,从简单的数据处理到复杂的业务逻辑,实现快速、高效、低成本的云上部署与运维。以下是一些关于使用函数计算产品的合集和要点,帮助你更好地理解和应用这一服务。
|
5月前
|
前端开发 Java 开发工具
JeecgBoot v3.7.0 all 版本发布,前后端合并一个仓库
合并前端源码和后端源码到一个 git 仓库,修复几个已知严重 bug。
67 2
|
6月前
|
JavaScript Java 测试技术
基于SpringBoot+Vue+uniapp的作业提交与查收系统的详细设计和实现(源码+lw+部署文档+讲解等)
基于SpringBoot+Vue+uniapp的作业提交与查收系统的详细设计和实现(源码+lw+部署文档+讲解等)
|
6月前
|
SQL 关系型数据库 MySQL
【Seata1.5.2 下载 & 配置 & 整合 & 踩坑 & 测试】—— 含各种踩坑记录(详细版)(上)
【Seata1.5.2 下载 & 配置 & 整合 & 踩坑 & 测试】—— 含各种踩坑记录(详细版)
956 0
|
6月前
|
前端开发 Java 应用服务中间件
ChaosBlade资源问题之把前端静态资源单独拉出来如何解决
ChaosBlade 是一个开源的混沌工程实验工具,旨在通过模拟各种常见的硬件、软件、网络、应用等故障,帮助开发者在测试环境中验证系统的容错和自动恢复能力。以下是关于ChaosBlade的一些常见问题合集:
|
6月前
|
SQL Java 数据库
【Seata1.5.2 下载 & 配置 & 整合 & 踩坑 & 测试】—— 含各种踩坑记录(详细版)(下)
【Seata1.5.2 下载 & 配置 & 整合 & 踩坑 & 测试】—— 含各种踩坑记录(详细版)(下)
353 0