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

news2024/11/17 6:01:58

全文参考: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

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

浅析三相异步电动机控制的电气保护

安科瑞 华楠 摘 要:要求三相异步电动机的控制系统不仅要保证电机正常启动和运行,完成制动操作,还要通过相关保护措施维护电动机的安全使用。基于此,本文以电动机电气保护作为研究对象,结合三相异步电动机的机械特点&…

electerm 跨平台的终端 /ssh/sftp 客户端

文章目录 electerm功能特性主题配色 electerm 每个程序员基本都离开SSH链接工具,目前市场上好用的基本都是收费的 给大家推荐一款国人开发的开源链接工具https://github.com/electerm/electerm 到目前为止star已经9.5K了,非常受欢迎 功能特性 支持ssh,telnet,serialport,本地和…

满满干货!搭建智能视频监控系统如何挑选前端设备?

在此前的文章中,小编也和大家讨论过如何选择适合场景需求又性价比高的摄像头。除了摄像头以外,智能监控系统的组成也少不了前端设备,今天就给大家介绍一下几大前端设备的区别与应用场景吧。 在智能视频监控中,前端设备一般分为四类…

功率放大器在介电弹性体测试中的应用案例

介电弹性体作为一种具有高介电常数的材料,近年来越来越受到人们的关注。由于其具有许多独特的性质,如高灵敏度、快速响应以及出色的循环稳定性,使其在许多领域都有广泛的应用,如电子皮肤、传感器和执行器等。在介电弹性体的测试中…

rocky8.9配置K8S集群kubernetes,centos同理

rocky8.9配置K8S集群 节点主机名IP地址mastertang1192.168.211.101node1tang2192.168.211.102node2tang3192.168.211.103 1)准备工作 全部主机都配置静态ip vi /etc/sysconfig/network-scriptsTYPEEthernet PROXY_METHODnone BROWSER_ONLYno BOOTPROTOstatic DE…

AI智能识别如何应用于单病种上报?

何为AI? AI是人工智能(Artificial Intelligence)的英文缩写,它是研究、开发用于模拟、延伸和扩展人的智能的理论、方法、技术及应用系统的一门新的技术科学。 人工智能是计算机科学的一个分支,该领域的研究包括机器人、语言识别…

C++算法 —— 贪心(3)

文章目录 1、买卖股票的最佳时机2、买卖股票的最佳时机Ⅱ3、K次取反后最大化的数组和4、按身高排序5、优势洗牌6、最长回文串7、增减字符串匹配 1、买卖股票的最佳时机 121. 买卖股票的最佳时机 这里最容易想到的就是暴力枚举,两层for循环,i 0&#xf…

TFA-Net

TFA SCA means ‘Self-Context Aggregation’ 作者未提供代码

JVM中如何实现垃圾收集

Java虚拟机(JVM)使用垃圾收集器(Garbage Collector)来管理内存,清理不再使用的对象以释放内存空间。垃圾收集的主要目标是自动化内存管理,使开发人员无需显式地释放不再使用的内存,从而降低了内…

EFAK-v3.0.1版部署与使用

一、前言 EFAK((Eagle For Apache Kafka,以前称为Kafka Eagle)用于在使用 Topic 的情况下监控 Kafka 集群。包含Offset 的产生、Lag的变化、Partition的分布、Owner、Topic的创建以及修改的时间等信息。 二、环境&安装包 官方下载连接E…

使用python 实现华为设备的SFTP文件传输

实验目的: 公司有一台CE12800的设备,管理地址位172.16.1.2,现在需要编写自动化脚本,通过SFTP实现简单的上传下载操作。 实验拓扑: 实验步骤: 步骤1:将本地电脑和ensp的设备进行桥接&#xff…

华为云之SFS弹性文件服务使用体验

华为云之SFS弹性文件服务使用体验 一、本次实践介绍1.1 实践环境简介1.2 本次实践目的 二、SFS弹性文件服务介绍2.1 SFS弹性文件服务简介2.2 SFS弹性文件服务特点 三、购买ECS弹性云服务器3.1 购买ECS弹性云服务器3.2 查看ECS弹性云服务器状态3.3 远程连接ECS3.4 检查操作系统版…

线程信息分析,生产环境问题

现象: 应用服务器启动不了 产生原因: 最近升级了,将单线程查询数据变成了多线程查询数据。 分析: 推测一、sql 查询时间太慢导致 排查sql 后发现,不是这个原因 取回线程启动过程的线程信息 发现线程死锁了&…

无线通信:基于深度强化学习

这里写自定义目录标题 异构蜂窝网络:用户关联和信道分配a stochastic gameMulti-Agent Q-Learning MethodMulti-Agent dueling double DQN Algorithm 分布式动态下行链路波束成形Limited-Information Exchange ProtocolDistributedDRL-Based DTDE Scheme for DDBCDi…

每日一题:LeetCode-102.二叉树的层序遍历

每日一题系列(day 03) 前言: 🌈 🌈 🌈 🌈 🌈 🌈 🌈 🌈 🌈 🌈 🌈 🌈 🌈 &#x1f50e…

使用Python对CSI相位进行矫正,并进行图像调整

一、前言 我记得我是最早将《MatLab对CSI的相位进行矫正》代码上传至网上的,后面陆续有人进行抄袭,不得已把一些细节和代码进行隐藏。今天整理之前Python代码的时候,发现一些不规范的问题,所以写了这篇博客。 二、使用Python对C…

大数据基础设施搭建 - Hive

文章目录 一、上传压缩包二、解压压缩包三、配置环境变量四、初始化元数据库4.1 配置MySQL地址4.2 拷贝MySQL驱动4.3 初始化元数据库4.3.1 创建数据库4.3.2 初始化元数据库 五、启动元数据服务metastore5.1 修改配置文件5.2 启动/关闭metastore服务 六、启动hiveserver2服务6.1…

手动实现 git 的 git diff 功能

这是 git diff 后的效果,感觉挺简单的,不就是 比较新旧版本,新增了就用 "" 显示新加一行,删除了就用 "-" 显示删除一行,修改了一行就用 "-"、"" 显示将旧版本中的该行干掉了并…

C#FlaUI.UIA实现发送微信消息原理

一 准备 .NetFramework 4.8 FlaUI.UIA3 4.0.0 FlaUInspect V1.3.0 1下载FlaUInspect https://github.com/FlaUI/FlaUInspect FlaUInspect V1.3.0 百度网盘下载 2 NuGet 引用 flaUI.UIA3 4.0.0 二代码部分 1 引用FlaUI using FlaUI.Core; using FlaUI.Core.Automatio…

深度学习基于Python+TensorFlow+Django的水果识别系统

欢迎大家点赞、收藏、关注、评论啦 ,由于篇幅有限,只展示了部分核心代码。 文章目录 一项目简介简介技术组合系统功能使用流程 二、功能三、系统四. 总结 一项目简介 # 深度学习基于PythonTensorFlowDjango的水果识别系统介绍 简介 该水果识别系统基于…