Nacos配置拉取及配置动态刷新原理【源码阅读】
Nacos配置拉取及配置刷新原理
一、初始化时获取配置文件
背景
SpringCloud项目中SpringBoot在启动阶段除了会创建SpringBoot容器,还会通过bootstrap.yml构建一个SpringCloud容器,之后会在准备上下文阶段通过SPI加载实现类后,会进行配置合并。
NacosPropertySourceLocator类
1、该类为拉取nacos配置文件的核心类,在结果SPI加载时NacosPropertySourceLocator作为PropertySourceLocator的实现类,也将被加载到PropertySourceBootstrapConfiguration中参加遍历。
2、最后来到NacosPropertySourceLocator类的locate方法,在该方法中会通过nacosConfigManager来获取ConfigService接口实现类
3、该方法结果调用最后会通过反射获取NacosConfigService对象的有参构造器创建并返回
4、回到上面的locate方法,最后开始加载配置文件,此处会加载三种类型配置文件,分别为共享配置文件、拓展配置文件、应用自身的配置文件。此时由于顺序的原因导致覆盖我们可以得出优先级为从下至上,而其中每一种配置文件的加载都会有三种加载方式,分别是从本地获取,再从nacos获取,为从nacos获取时网络异常等原因导致获取失败,最后一种获取方式为从快照获取(快照的来源为每一种成功从nacos获取后保存)。
5、最后都将使用上文的NacosConfigService对象getConfigInner方法实现三种方式的获取
private String getConfigInner(String tenant, String dataId, String group, long timeoutMs) throws NacosException {group = blank2defaultGroup(group);ParamUtils.checkKeyParam(dataId, group);ConfigResponse cr = new ConfigResponse();cr.setDataId(dataId);cr.setTenant(tenant);cr.setGroup(group);// 优先使用本地配置String content = LocalConfigInfoProcessor.getFailover(agent.getName(), dataId, group, tenant);if (content != null) {LOGGER.warn("[{}] [get-config] get failover ok, dataId={}, group={}, tenant={}, config={}", agent.getName(),dataId, group, tenant, ContentUtils.truncateContent(content));cr.setContent(content);String encryptedDataKey = LocalEncryptedDataKeyProcessor.getEncryptDataKeyFailover(agent.getName(), dataId, group, tenant);cr.setEncryptedDataKey(encryptedDataKey);configFilterChainManager.doFilter(null, cr);content = cr.getContent();return content;}try {ConfigResponse response = worker.getServerConfig(dataId, group, tenant, timeoutMs);cr.setContent(response.getContent());cr.setEncryptedDataKey(response.getEncryptedDataKey());configFilterChainManager.doFilter(null, cr);content = cr.getContent();return content;} catch (NacosException ioe) {if (NacosException.NO_RIGHT == ioe.getErrCode()) {throw ioe;}LOGGER.warn("[{}] [get-config] get from server error, dataId={}, group={}, tenant={}, msg={}",agent.getName(), dataId, group, tenant, ioe.toString());}LOGGER.warn("[{}] [get-config] get snapshot ok, dataId={}, group={}, tenant={}, config={}", agent.getName(),dataId, group, tenant, ContentUtils.truncateContent(content));content = LocalConfigInfoProcessor.getSnapshot(agent.getName(), dataId, group, tenant);cr.setContent(content);String encryptedDataKey = LocalEncryptedDataKeyProcessor.getEncryptDataKeyFailover(agent.getName(), dataId, group, tenant);cr.setEncryptedDataKey(encryptedDataKey);configFilterChainManager.doFilter(null, cr);content = cr.getContent();return content;
}
6、最后将配置文件中的信息都保存到composite中返回。
以上为项目启动时获取的配置文件流程
配置刷新原理
背景
当远程 Nacos Config Server 中的配置信息发生了变更,我们springboot应用作为Nacos Config Client 是如何感知到的呢?
此处我们需要知道一点前置知识,那就是两个数据消费模型,push模型和pull模型。
- Push 模型:当 Server 端的数据发生了变更,其会主动将更新推送给 Client。Push 模型 适合于 Client 数量不多,且 Server 端数据变化比较频繁的场景。其实时性较好,但其需要维护长连接,占用系统资源。
- Pull 模型:需要 Client 定时查看 Server 端数据是否更新。其实时性不好,且可能会产生数据更新的丢失。
那nacos采用的是哪种模型呢?
其实都不是,Nacos采用了长轮询模型实现的变更通知。
那什么是长轮询呢?
- 长轮询模型整合了 Push 与 Pull 模型的优势。Client 仍定时发起 Pull 请求,查看 Server 端数据是否更新。若发生了更新,则 Server 立即将更新数据以响应的形式发送给 Client 端; 若没有发生更新,Server 端不会发送任何信息,但其会临时性的保持住这个连接一段时间。 若在此时间段内,Server 端数据发生了变更,这个变更就会触发 Server 向 Client 发送变更结 果。这次发送的执行,就是因为长连接的存在。若此期间仍未发生变更,则放弃这个连接。 等待着下一次 Client 的 Pull 请求。 长轮询模型,是 Push 与 Pull 模型的整合,既减少了 Push 模型中长连接的被长时间维护 的时间,又降低了 Pull 模型实时性较差的问题。
- Nacos断开连接默认为3秒
2.1、在上文第2、3步我们提到NacosPropertySourceLocator会通过反射获取到NacosConfigService类的全程构造方法并且构造出NacosConfigService对象。此时我们来看看NacosConfigService类的构造方法。注意该方法中我画绿圈的worker属性构造。该属性即为配置刷新的核心。
2.2、来到ClientWorker构造方法,我们可以看到其中有两个周期线程池,其中executor线程池就是为了控制发出配置检测的间隔,为10秒一次,断开连接默认为3秒
2.3、接下来我们看到发出配置检测请求的checkConfigInfo()方法
ParamUtil.getPerTaskConfigSize()的值为3000,currentLongingTaskCount默认为0,所以正常情况下都只是循环一次
2.4、接下来我们点到LongPollingRunnable看着像长轮询的类,该类实现了Runnable接口,查看其run方法。
以下是使用本地配置时检测是否更新
2.5、然后开始检测并获得远程的变化了的key
2.6、然后获取到对应的value设置新值且计算md5,md5即用于比较是否发现变化,注意其中的setContent方法
public void setContent(String content) {this.content = content;this.md5 = getMd5String(this.content);
}
2.7、随后开始检测并开启下一次任务
2.8、接下来我们打开检测是否变化的方法checkListenerMd5()
2.9、发现发生变化后将会调用safeNotifyListener方法,safeNotifyListener方法则会触发刷新的方法。
补充
- 3.1、在介绍该方法之前我们补充以下之前遗漏的初始化监听器的步骤,在springboot准备完环境后将会发出ApplicationReadyEvent事件。而在NacosConfigAutoConfiguration配置类中注入了一个NacosContextRefresher对象
- 3.2、我们打开NacosContextRefresher对象,看到该类实现了ApplicationListener接口,并且监听事件刚好为上步骤中提到的ApplicationReadyEvent事件。
- 3.3、接下来我们看看他的监听方法中做了什么,看到使用了CAS来控制registerNacosListenersForApplications方法的调用来注册Nacos监听器
- 3.4、接下来接着查看registerNacosListenersForApplications方法如何绑定的监听器,我们看到是调用registerNacosListener()方法给每一个dataId绑定监听器。
- 3.5、接下来我们点到registerNacosListener方法中,看到给使用dataId与groupId生成的key绑定一个AbstractSharedListener抽象实现类的监听器,需要注意的时候该实现类中的实现的innerReceive()方法会发出一个RefreshEvent事件,该事件会触发带有@RefreshScope注解的类刷新。
- 3.6、至此我们看到了绑定监听器的过程,接下来我们回到之前的地方。
2.10、此时回到CacheData类的checkListenerMd5()方法中,该方法调用了safeNotifyListener()方法
2.11、打开safeNotifyListener()方法,看到Listener被强转为AbstractSharedListener修改了其中的dataId与group
2.12、随后调用了AbstractSharedListener类的receiveConfigInfo()方法,该方法中我们在上文提到过该方法中会发布出一个RefreshEvent事件,该事件会触发带有@RefreshScope注解的类刷新。
-
@RefreshScope概述
- @RefreshScope注解标注了@Scope注解,井默认了ScopedProxyMode.TARGET_CLASS
属性,此属性的功能就是创建一个代理,在每次调用的时候都用它来调用GenericScope#get方法来获取bean对象 - 在GenericScope里面包装了一个内部类BeanLifecycleWrapperCache来对加了
@RefreshScope的bean进行缓存,使其在不刷新时获取的都是同一个对象 - 如属性发生变更会调用ContextRefresher#frefresh()一>RefreshScope#refreshAll()进行缓存
清理方法调用,并发送刷新事件通知一>调用GenericScope#destroy()实现清理缓存 - 当下一次使用此bean对象时,代理对象会调用GenericScoper#get(String name,
ObjectFactory<?>objectFactory)方法创建一个新的bean对象,井存入缓存中,此时新对象因为Spring的装配机制就是新的属性了
- @RefreshScope注解标注了@Scope注解,井默认了ScopedProxyMode.TARGET_CLASS
相关文章:

Nacos配置拉取及配置动态刷新原理【源码阅读】
Nacos配置拉取及配置刷新原理 一、初始化时获取配置文件 背景 SpringCloud项目中SpringBoot在启动阶段除了会创建SpringBoot容器,还会通过bootstrap.yml构建一个SpringCloud容器,之后会在准备上下文阶段通过SPI加载实现类后,会进行配置合并…...

第十届省赛——9等差数列(集合做法)
题目:试题 I: 等差数列时间限制: 1.0s 内存限制: 512.0MB 本题总分:25 分【问题描述】数学老师给小明出了一道等差数列求和的题目。但是粗心的小明忘记了一部分的数列,只记得其中 N 个整数。现在给出这 N 个整数,小明想知道包含这…...

《数据分析-JiMuReport03》JiMuReport报表设计入门介绍-新建报表
报表设计 1 新建报表 1.1 创建新的数据报表 以数据报表为例,简单介绍创建报表的过程 1.2 进入报表设计页面 如下图可见,主要分为四个模块: 模块一(左) 数据集管理报表信息数据字典 模块二(右) 这部分是对数据报表的进一步优化 模块三(上…...

从功能测试进阶自动化测试,爆肝7天整理出这一份超全学习指南【附网盘资源】
因为我最近在分享自动化测试技术,经常被问到:功能测试想转自动化,请问应该怎么入手?有没有好的资源推荐?那么,接下来我就结合自己的经历聊一聊我是如何在工作中做自动化测试的。(学习路线和网盘…...

CNN神经网络——手写体识别
目录 Load The Datesets Defining,Training,Measuring CNN Algorithm Datasets GRAET HONOR TO SHARE MY KNOWLEDGE WITH YOU This paper is going to show how to use keras to relize a CNN model for digits classfication Load The Datesets The datasets files are …...

python调试模块ipdb
1. 调试python ipdb是用来python中用以交互式debug的模块,可以直接利用pip安装; 其功能类似于pycharm中 python控制台, 而使用ipdb 的优点,便是直接在代码中调试, 避免了在python控制台,或者重新设置一些简单变量。…...

【数据库】聊聊MySQL的日志,binlog、undo log、redo log
日志 在数据库中,如何保证数据的回滚,以及数据同步,系统宕机后可以恢复到原来的状态,其实就是依靠日志。 其中bin log是Server层特有的,redo log是Innodb存储引擎特有的。 bin log 是逻辑日志,主要记录这条…...

aws dynamodb java低等级api和高级客户端api的使用
参考资料 https://docs.amazonaws.cn/zh_cn/sdk-for-java/latest/developer-guide/setup-project-maven.html 初始化环境 创建maven项目 mvn org.apache.maven.plugins:maven-archetype-plugin:3.1.2:generate \-DarchetypeArtifactId"maven-archetype-quickstart&quo…...

Kafka中那些巧妙的设计
一、kafka的架构 Kafka是一个分布式、多分区、基于发布/订阅模式的消息队列(Message Queue),具有可扩展和高吞吐率的特点。 kafka中大致包含以下部分: Producer: 消息生产者,向 Kafka Broker 发消息的客户…...

《JavaEE》进程和线程的区别和联系
👑作者主页:Java冰激凌 📖专栏链接:JavaEE 目录 进程是什么? 线程是什么? 进程和线程之间的联系~ ps1:假设我们当前的大兴国际机场有一条登机口可以登入飞机 ps2:我们为…...

Matlab生成sinc信号
Matlab生成sinc信号 在Matlab中生成sinc信号非常容易。首先,我们需要了解什么是sinc波形。 sinc波形是一种理想的信号,它在时域上是一个宽度为无穷的矩形函数,而在频域上则是一个平的频谱。它的公式为: sinc(x)sin(πx)πx\…...
进程与线程区别与联系
进程与线程的区别与联系线程线程介绍为什么要有线程呢?线程与进程的区别于联系(重点)线程 线程介绍 我们知道进程就是运行起来的程序, 那线程又是什么呢? 一个线程就是一个 “执行流”. 每个线程之间都可以按照顺序执行自己的代码. 多个线程之间 “同时” 执行着多份代码. …...
使用vbscript.regexp实现VBA代码格式化
Office自带的VBE在编辑代码时,没有自动完成代码缩进的功能,而我们在网上找到的VBA代码,经常没有实现良好的自动缩进,复制到VBE后,可读性较差。本文介绍的宏,通过使用vbscript.regexp对象,利用正…...
选择结构习题:百分值转换成其相应的等级
Description 编一程序,输入一个百分制的成绩(整数类型),按要求输出相应的字符串信息,对应关系为: excellent 90-100 good 80-89 middle 70-79 pass 60-69 fail 60以下或100以上 Input 输入仅一行&…...

c# 源生成器
本文概述了 .NET Compiler Platform(“Roslyn”)SDK 附带的源生成器。 通过源生成器,C# 开发人员可以在编译用户代码时检查用户代码。 生成器可以动态创建新的 C# 源文件,这些文件将添加到用户的编译中。 这样,代码可以…...

[N1CTF 2018]eating_cms1
一个cms,先打开环境试了一下弱口令,无效,再试一下万能密码,告诉我有waf,先不想怎么绕过,直接开扫(信息收集)访问register.php注册一个账号进行登录上面的链接尝试用php读文件http://…...

数据结构与算法基础(王卓)(15):KMP算法详解(含速成套路和详细思路剖析)
如果时间不够,急(忙)着应付考试没心思看,直接参考(照抄)如下套路: PART 1:关于next [ j ] PPT:P30 根据书上以及视频上给出的思路(提醒)&#x…...

【互联网架构】聊一聊所谓的“跨语言、跨平台“
文章目录序跨语言跨平台【饭后杂谈】为什么有人说Java的跨平台很鸡肋?序 很多技术都具有跨语言、跨平台的特点 比如JSON是跨语言的、Java是跨平台的、UniAPP、Electron是跨平台的 跨语言和跨平台,是比较重要的一个特性。这些特性经常能够决定开发者是否…...

1.JVM常识之 类加载器
1.jvm组成 JVM组成: 1.类加载器 2.运行时数据区 3.执行引擎 4.本地库接口 各组件的作用: 首先通过类加载器(ClassLoader)会把 Java 代码转换成字节码,运行时数据区(Runtime Data Area)再把字节码…...

一天搞定《AI工程师的PySide2 PyQt5实战开发手册》
PySide2/PySide6、PyQt5/PyQt6:都是基于Qt 的Python库,可以形象地这样说,PySide2 是Qt的 亲儿子(Qt官方开发的) , PyQt5 是Qt还没有亲儿子之前的收的 义子 (Riverbank Computing这个公司开发的,有商业版权限…...

Chapter03-Authentication vulnerabilities
文章目录 1. 身份验证简介1.1 What is authentication1.2 difference between authentication and authorization1.3 身份验证机制失效的原因1.4 身份验证机制失效的影响 2. 基于登录功能的漏洞2.1 密码爆破2.2 用户名枚举2.3 有缺陷的暴力破解防护2.3.1 如果用户登录尝试失败次…...

相机Camera日志实例分析之二:相机Camx【专业模式开启直方图拍照】单帧流程日志详解
【关注我,后续持续新增专题博文,谢谢!!!】 上一篇我们讲了: 这一篇我们开始讲: 目录 一、场景操作步骤 二、日志基础关键字分级如下 三、场景日志如下: 一、场景操作步骤 操作步…...

Python实现prophet 理论及参数优化
文章目录 Prophet理论及模型参数介绍Python代码完整实现prophet 添加外部数据进行模型优化 之前初步学习prophet的时候,写过一篇简单实现,后期随着对该模型的深入研究,本次记录涉及到prophet 的公式以及参数调优,从公式可以更直观…...

CMake 从 GitHub 下载第三方库并使用
有时我们希望直接使用 GitHub 上的开源库,而不想手动下载、编译和安装。 可以利用 CMake 提供的 FetchContent 模块来实现自动下载、构建和链接第三方库。 FetchContent 命令官方文档✅ 示例代码 我们将以 fmt 这个流行的格式化库为例,演示如何: 使用 FetchContent 从 GitH…...
[Java恶补day16] 238.除自身以外数组的乘积
给你一个整数数组 nums,返回 数组 answer ,其中 answer[i] 等于 nums 中除 nums[i] 之外其余各元素的乘积 。 题目数据 保证 数组 nums之中任意元素的全部前缀元素和后缀的乘积都在 32 位 整数范围内。 请 不要使用除法,且在 O(n) 时间复杂度…...
css3笔记 (1) 自用
outline: none 用于移除元素获得焦点时默认的轮廓线 broder:0 用于移除边框 font-size:0 用于设置字体不显示 list-style: none 消除<li> 标签默认样式 margin: xx auto 版心居中 width:100% 通栏 vertical-align 作用于行内元素 / 表格单元格ÿ…...
全面解析各类VPN技术:GRE、IPsec、L2TP、SSL与MPLS VPN对比
目录 引言 VPN技术概述 GRE VPN 3.1 GRE封装结构 3.2 GRE的应用场景 GRE over IPsec 4.1 GRE over IPsec封装结构 4.2 为什么使用GRE over IPsec? IPsec VPN 5.1 IPsec传输模式(Transport Mode) 5.2 IPsec隧道模式(Tunne…...

html-<abbr> 缩写或首字母缩略词
定义与作用 <abbr> 标签用于表示缩写或首字母缩略词,它可以帮助用户更好地理解缩写的含义,尤其是对于那些不熟悉该缩写的用户。 title 属性的内容提供了缩写的详细说明。当用户将鼠标悬停在缩写上时,会显示一个提示框。 示例&#x…...
动态 Web 开发技术入门篇
一、HTTP 协议核心 1.1 HTTP 基础 协议全称 :HyperText Transfer Protocol(超文本传输协议) 默认端口 :HTTP 使用 80 端口,HTTPS 使用 443 端口。 请求方法 : GET :用于获取资源,…...
纯 Java 项目(非 SpringBoot)集成 Mybatis-Plus 和 Mybatis-Plus-Join
纯 Java 项目(非 SpringBoot)集成 Mybatis-Plus 和 Mybatis-Plus-Join 1、依赖1.1、依赖版本1.2、pom.xml 2、代码2.1、SqlSession 构造器2.2、MybatisPlus代码生成器2.3、获取 config.yml 配置2.3.1、config.yml2.3.2、项目配置类 2.4、ftl 模板2.4.1、…...