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

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

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

亚马逊Listing怎么写!亲身经验分享

亚马逊运营的重要环节之一,listing的攥写,可以决定了产品的搜索排名,用户的点击率和转化率,那么如果你的产品排名或者转化不理想的情况,可以考虑对listing进行优化,在关键词过多和语句流程通顺的情况下&…

Spring Cloud LoadBalancer 简单介绍与实战

前言 本文为SpringCloud的学习笔记,如有错误,希望各位高手能指出,主要介绍SpringCloudLoadBalancer的基本概念和实战 文章目录 前言什么是LoadBalancer负载均衡分类服务端负载均衡客户端负载均衡服务端负载均衡和客户端负载均衡的优缺点 常见…

数据挖掘之PCA-主成分分析

PCA的用处:找出反应数据中最大变差的投影(就是拉的最开)。 在减少需要分析的指标同时,尽量减少原指标包含信息的损失,以达到对所收集数据进行全面分析的目的 但是什么时候信息保留的最多呢?具体一点&#…

SQLite 和 SQLiteDatabase 的使用

实验七:SQLite 和 SQLiteDatabase 的使用 7.1 实验目的 本次实验的目的是让大家熟悉 Android 中对数据库进行操作的相关的接口、类等。SQLiteDatabase 这个是在 android 中数据库操作使用最频繁的一个类。通过它可以实现数据库的创建或打开、创建表、插入数据、删…

Stable Video Diffusion重磅发布,快来看看哪些功能

本周,有关 OpenAI 宫斗的报道占据了Ai圈版面的主导地位,吃够了奥特曼的大瓜。我们来看看Stability AI刚发布的Stable Video Diffusion,这是一种通过对现有图像进行动画处理来生成视频的 AI 模型。基于 Stability 现有的Stable Diffusion文本到…

优秀的5款字体设计软件推荐

字体设计作为设计中的一个重要模块,如果字体软件选择正确,将给字体设计工作带来极大的便利,易于使用的字体设计软件,可以创造出优秀的排版设计。在日常工作中,设计师可能会在字体网站上下载字体,然后安装字…

Grafana 如何实现雷达图

程序员的公众号:源1024,获取更多资料,无加密无套路! 最近整理了一波电子书籍资料,包含《Effective Java中文版 第2版》《深入JAVA虚拟机》,《重构改善既有代码设计》,《MySQL高性能-第3版》&…

Windows核心编程 进程

目录 一、进程概述 二、创建进程相关API Winexec ShellExecute CreateProcess 三、进程退出相关API ExitProcess TerminateProcess GetCurrentProcess GetExitCodeProcess 四、如何理解虚拟内存空间 五、关于UAC 一、进程概述 进程:正在运行的程序 程…

[C/C++]数据结构 循环队列

前言: 队列是一种具有先进先出特性的结构,但是当数据出队列以后,前面的空间就无法再次利用了,循环队列就可以解决这个问题 一:概念及结构: 1.循环队列概念 循环队列是一种线性数据结构,其操作表现基于 FIFO(先进先出)原则并且队尾被连接在队…

Intel Software Guard Extensions简介(一)

文章目录 前言一、简介二、enclave相互作用与保护三、enclave生命周期四、数据结构和enclave操作五、enclave page cache六、enclave instructions and intel sgx参考资料 前言 最近开始研究Intel SGX硬件特性,记录下研究过程。 目前安全性主要关注对存储中的静止…

一篇总结 Linux 系统启动的几个汇编指令

学习 Linux 系统启动流程,必须熟悉几个汇编指令,总结给大家。 这里不是最全的,只列出一些最常用的汇编指令。 一.数据处理指令 1.数据传送指令 【MOV指令】 把一个寄存器的值(立即数)赋给另一个寄存器,或者将一个…

使用pt-query-digest分析慢查询日志

介绍 pt-query-digest 属于 Percona Toolkit 工具集中较为常用的工具,用于分析 slow log,可以分析 MySQL 数据库的 binary log 、 general log 日志,同时也可以使用 show processlist 或从 tcpdump 抓取的 MySQL 协议数据来进行分析。 安装…