Apollo接入配置中心 -- 源码分析之如何获取配置

全文参考:https://mp.weixin.qq.com/s/G5BV5BIdOtB3LlxNsr4ZDQ

https://blog.csdn.net/crystonesc/article/details/106630412

https://www.cnblogs.com/deepSleeping/p/14565774.html

背景:近期在接入行内配置中心,因此对配置的加载接入有了一些兴趣,由于当时接入Apollo配置中心出现过不少问题,所以做以下整理。

此处参考:https://blog.csdn.net/crystonesc/article/details/106630412

​ 使用Apollo接入,但由于是SpringMVC项目,且原先部分配置与Apollo加载存在冲突,无法使用SpringBean方式管理接入配置。因此使用Apollo中获取的配置方式接入。具体代码如下:

Config config = ConfigService.getConfig("application.properties"); 
String userNmae = config.getProperty("st.username", null);
  1. 此处 ConfigService.getConfig() 传入的是namespace,在Apollo配置中心默认是 application.properties
  2. getConfig()追溯到最终的实现类,通过namespaces。

ConfigService.getConfig()方法是在ConfigService类中的,ConfigService是一个单例,也就是说对于应用程序来说只会有一个ConfigService的实例,并且实例是被通过私有静态变量被持有在ConfigService当中。

private static final ConfigService s_instance = new ConfigService();

​ 同时我们看到ConfigService持有两个属性ConfigManager和ConfigRegistry,其中ConfigManager是配置(ConfigManager)的管理器,ConfigRegistry用于手工配置注入,这两个属性的初始化均是通过ApolloInjector来注入的。ConfigService中的另外一个方法getAppConfig,该方法用户获取application配置文件的内容(apollo中创建的默认配置文件(namespace))。而getAppConfig中会实际调用getConfig获取配置,getConfig则是通过ConfigManager去获取配置。

// 通过namespace获取Config,首先从m_configs缓存中获取,
// 如果没有获取则通过ConfigFacotryManager获取ConfigFactory并创建Config
public Config getConfig(String namespace) {
    Config config = m_configs.get(namespace);
    if (config == null) {
        synchronized (this) {
    	    config = m_configs.get(namespace);
            if (config == null) {
    	        ConfigFactory factory = m_factoryManager.getFactory(namespace);
    	        config = factory.create(namespace);
    	        m_configs.put(namespace, config);
    	    }
       }
   }
    return config;
}

​ 源码中,关于配置的获取,可以追溯到config = factory.create(namespace);

ConfigFactory factory = m_factoryManager.getFactory(namespace);
config = factory.create(namespace);
m_configs.put(namespace, config);

​ 也就是说,在此完成了配置的创建和获取。

public Config create(String namespace) {
    // 判断namespace的文件类型
    ConfigFileFormat format = this.determineFileFormat(namespace);
    ConfigRepository configRepository = null;
    // 判断文件属性是否不为 properties,需要注意 format !=
    if (ConfigFileFormat.isPropertiesCompatible(format) && format != ConfigFileFormat.Properties) {
        configRepository = this.createPropertiesCompatibleFileConfigRepository(namespace, format);
    } else {
        // application.properties 会走到这个方法
        configRepository = this.createConfigRepository(namespace);
    }
    logger.debug("Created a configuration repository of type [{}] for namespace [{}]", configRepository.getClass().getName(), namespace);
    return this.createRepositoryConfig(namespace, (ConfigRepository)configRepository);
}

​ 在 configRepository = this.createConfigRepository(namespace); 方法中,实际上会去访问 本地持久化的 apollo 配置,默认地址为 /opt/data。

注:以下代码之间非一个类中,为方便关联查看进行了位置调整。

LocalFileConfigRepository createLocalConfigRepository(String namespace) {
    if (this.m_configUtil.isInLocalMode()) {
        logger.warn("==== Apollo is in local mode! Won't pull configs from remote server for namespace {} ! ====", namespace);
        return new LocalFileConfigRepository(namespace);
    } else {
        // 此处往下调用
        return new LocalFileConfigRepository(namespace, this.createRemoteConfigRepository(namespace));
    }
}

public LocalFileConfigRepository(String namespace, ConfigRepository upstream) {
    this.m_sourceType = ConfigSourceType.LOCAL;
    this.m_namespace = namespace;
    this.m_configUtil = (ConfigUtil)ApolloInjector.getInstance(ConfigUtil.class);
    // 此处往下调用
    this.setLocalCacheDir(this.findLocalCacheDir(), false);
    this.setUpstreamRepository(upstream);
    this.trySync();
}

private File findLocalCacheDir() {
    try {
        // 此处往下调用
        String defaultCacheDir = this.m_configUtil.getDefaultLocalCacheDir();
        Path path = Paths.get(defaultCacheDir);
        if (!Files.exists(path, new LinkOption[0])) {
            Files.createDirectories(path);
        }
        if (Files.exists(path, new LinkOption[0]) && Files.isWritable(path)) {
            return new File(defaultCacheDir, "/config-cache");
        }
    } catch (Throwable var3) {
    }
    return new File(ClassLoaderUtil.getClassPath(), "/config-cache");
}

public String getDefaultLocalCacheDir() {
    String cacheRoot = this.getCustomizedCacheRoot();
    if (!Strings.isNullOrEmpty(cacheRoot)) {
        return cacheRoot + File.separator + this.getAppId();
    } else {
        // 此处可以看到会去默认的 /opt/data/ 寻找缓存apollo配置中心的应用配置
        cacheRoot = this.isOSWindows() ? "C:\\opt\\data\\%s" : "/opt/data/%s";
        return String.format(cacheRoot, this.getAppId());
    }
}

​ 在回到 create()方法 返回值为return this.createRepositoryConfig(namespace, (ConfigRepository)configRepository);,定睛一看,实际上最后返回了DefaultConfig。

protected Config createRepositoryConfig(String namespace, ConfigRepository configRepository) {
    return new DefaultConfig(namespace, configRepository);
}
return new DefaultConfig(namespace, configRepository);

​ 而DefaultConfig的构造方法中 this.loadFromResource(this.m_namespace); 完成了配置的读取。

public DefaultConfig(String namespace, ConfigRepository configRepository) {
    this.m_sourceType = ConfigSourceType.NONE;
    this.m_namespace = namespace;
    this.m_resourceProperties = this.loadFromResource(this.m_namespace);
    this.m_configRepository = configRepository;
    this.m_configProperties = new AtomicReference();
    this.m_warnLogRateLimiter = RateLimiter.create(0.017);
    this.initialize();
}

在 loadFromResource(this.m_namespace); 中,取到了在 META-INF/config/ 下的为 namespace 的配置。

private Properties loadFromResource(String namespace) {
    String name = String.format("META-INF/config/%s.properties", namespace);
    InputStream in = ClassLoaderUtil.getLoader().getResourceAsStream(name);
    Properties properties = null;
    if (in != null) {
        properties = this.propertiesFactory.getPropertiesInstance();
        try {
            properties.load(in);
        } catch (IOException var14) {
            Tracer.logError(var14);
            logger.error("Load resource config for namespace {} failed", namespace, var14);
        } finally {
            try {
                in.close();
            } catch (IOException var13) {
            }
        }
    }
    return properties;
}

至此配置完成了获取。配置如何刷新、拉取,有时间会再次进行更新,还请各位看官耐心等待。

小结: img

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/186669.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

p12 63.删除无头结点无头指针的循环链表中所有值为x的结点 桂林电子科技大学2015年 (c语言代码实现)注释详解

本题代码如下 void delete(linklist* L, int x) {lnode* p *L, * q *L;while (p->next ! q)// 从第一个结点开始遍历链表,直到尾结点的前一个结点{if (p->next->data x)//判断是否等于x{lnode* r p->next;//将r指向x的位置p->next r->next;…

JoyT的科研之旅第一周——科研工具学习及论文阅读收获

CiteSpace概述 CiteSpace 是一个用于可视化和分析科学文献的工具,它专门针对研究者进行文献回顾和趋势分析。CiteSpace 的核心功能是创建文献引用网络,这些网络揭示了研究领域内各个文献之间的相互关系。使用 CiteSpace 可以为论文研究做出贡献的几种方…

Linux常用命令——bind命令

在线Linux命令查询工具 bind 显示或设置键盘按键与其相关的功能 补充说明 bind命令用于显示和设置命令行的键盘序列绑定功能。通过这一命令,可以提高命令行中操作效率。您可以利用bind命令了解有哪些按键组合与其功能,也可以自行指定要用哪些按键组合…

【Proteus仿真】【STM32单片机】智能垃圾桶设计

文章目录 一、功能简介二、软件设计三、实验现象联系作者 一、功能简介 本项目使用Proteus8仿真STM32单片机控制器,使用报警模块、LCD1602液晶模块、按键模块、人体红外传感器、HCSR04超声波、有害气体传感器、SG90舵机等。 主要功能: 系统运行后&…

python基础-numpy

numpy中shape (1,X) 和 (X,)的区别 参考 首先放结论:shape(x,)是一维数组,ndim1,[1,2,3,…x] ;shape(1,x)是二维?数组,ndim2,[[1,2,3,…n]] >>> import numpy as np >>> a…

常见树种(贵州省):016杜鹃、含笑、桃金娘、金丝桃、珍珠花、观光木

摘要:本专栏树种介绍图片来源于PPBC中国植物图像库(下附网址),本文整理仅做交流学习使用,同时便于查找,如有侵权请联系删除。 图片网址:PPBC中国植物图像库——最大的植物分类图片库 一、杜鹃 …

记录华为云服务器(Linux 可视化 宝塔面板)-- 安全组篇

文章目录 前言安全组说明安全组的特性安全组的应用场景 进入安全组添加基本规则添加自定义规则如有启发,可点赞收藏哟~ 前言 和windows防火墙类似,安全组是一种虚拟防火墙,具备状态检测和数据包过滤功能,可以对进出云服务器的流量…

5种主流API网关技术选型,yyds!

API网关是微服务项目的重要组成部分,今天来聊聊API网关的技术选型,有理论,有实战。 不 BB,上文章目录: 1 API网关基础 1.1 什么是API网关 API网关是一个服务器,是系统的唯一入口。 从面向对象设计的角度…

Linux加强篇002-部署Linux系统

目录 前言 1. shell语言 2. 执行命令的必备知识 3. 常用系统工作命令 4. 系统状态检测命令 5. 查找定位文件命令 6. 文本文件编辑命令 7. 文件目录管理命令 前言 悟已往之不谏,知来者之可追。实迷途其未远,觉今是而昨非。舟遥遥以轻飏&#xff…

测试用例的缝缝补补

📑打牌 : da pai ge的个人主页 🌤️个人专栏 : da pai ge的博客专栏 ☁️山水速疾来去易,襄樊镇固永难开 ☁️定位页面的元素 参数:抽象类By里…

四、防火墙-NAT Server

学习防火墙之前,对路由交换应要有一定的认识 NAT Server1.1.基本原理1.2.多出口场景下的NAT Server1.3.源进源出 —————————————————————————————————————————————————— NAT Server 一般对用户提供一些可访问的…

apipost接口200状态码,浏览器控制台500状态码

后端 url 登录login方法 login(){this.$refs.loginForm.validate(async valid > {if (!valid) return// 由于data属性是一个json对象,需要进行解构赋值{data:result},进行状态码判断const {data: result} await this.$http.post(/api/doLogin,this.…

普通程序员 VS 高级程序员!

见字如面,我是军哥! 最近有读者朋友问我,普通程序员和高级程序员到底有什么区别?我说最关键的3点,你看看是否认同哈~ 1、需求评审的能力 普通程序员,就是根据产品经理提的需求细节,开…

vue2:mixin混入的使用

前言 在使用vue2开发业务时,难免会遇到一些多组件公用的方法和基础的数值。 比如你的页面里面有很多相似的列表展示,分页器都是默认1页10行,都需要调用某个公共的接口,或者某一个操作函数很多页面都需要调用。 这个时候,就可以使用mixin和extend这两个api,将公共的数据和代码…

【鸿蒙应用ArkTS开发系列】- 云开发入门实战二 实现省市地区三级联动地址选择器组件(下)

文章目录 概述端云调用流程端侧集成AGC SDK端侧省市地区联动的地址选择器组件开发创建省市数据模型创建省市地区视图UI子组件创建页面UI视图Page文件 打包测试总结 概述 我们在前面的课程,对云开发的入门做了介绍,以及使用一个省市地区联动的地址选择器…

【点云surface】无序点云快速三角化

1 介绍 GreedyProjectionTriangulation 是一种基于局部二维投影的三维点贪婪三角剖分算法的实现。它假定局部表面光滑,不同点密度区域之间的过渡相对平滑。 GreedyProjectionTriangulation算法的基本思想是通过逐步投影点云数据到一个三角化网格上来进行重建。它首…

python-opencv轮廓检测(外轮廓检测和全部轮廓检测,计算轮廓面积和周长)

python-opencv轮廓检测(外轮廓检测和全部轮廓检测,计算轮廓面积和周长) 通过cv2.findContours,我们可以进行轮廓检测,当然也有很多检测模式,我们可以通过选择检测模式,进行外轮廓检测&#xff…

Redis与Mysql的数据强一致性方案

目的 Redis和Msql来保持数据同步,并且强一致,以此来提高对应接口的响应速度,刚开始考虑是用mybatis的二级缓存,发现坑不少,于是决定自己搞 要关注的问题点 操作数据必须是唯一索引 如果更新数据不是唯一索引&#…

极智项目 | 实战实时、多人2D人体姿态识别之OpenPose

欢迎关注我的公众号 [极智视界],获取我的更多经验分享 大家好,我是极智视界,本文来介绍 实战实时、多人2D人体姿态识别之OpenPose。 本文介绍的 实战实时、多人2D人体姿态识别之OpenPose,提供完整的可以一键执行的项目工程源码&…

2023 hnust 湖南科技大学 信息安全管理课程 期中考试 复习资料

前言 ※老师没画重点的补充内容★往年试卷中多次出现或老师提过的,很可能考该笔记是奔着及格线去的,不是奔着90由于没有听过课,部分知识点不一定全,答案不一定完全正确 题型 试卷有很多题是原题 判断题(PPT&#xff…