一、为什么有这课程
Spring Cloud Alibaba 新版本中Seata 1.5.2和Nacos 2.1.0 在性能和使用方面都有很大提升,这节课将从使用和源码的角度详细讲解这两个框架。
二、设计注册中心
1、分布式框架的注意点:三高架构
-
高可用
高可用性(High Availability)通常来描述一个系统经过专门的设计,从而减少停工时间,而保持其服务的高度可用性(一直都能用)。
解决方案:集群
-
高并发
高并发(High Concurrency)是互联网分布式系统架构设计中必须考虑的因素之一,它通常是指,通过设计保证系统能够同时并行处理很多请求。 高并发相关常用的一些指标有响应时间(Response Time),吞吐量(Throughput),每秒查询率QPS(Query Per Second),并发用户数等。
解决方案:
- 垂直扩展:增强单机硬件性能
- 水平扩展:
-
高性能
高性能(High Performance)就是指程序处理速度快,所占内存少,cpu占用率低。
解决方案:
指程序处理速度快: 这里设计我们数据存储结构、访问机制、集群同步
2、注册中心的设计
- 服务注册
- 注册表结构设计
- 服务发现
- 服务订阅
- 服务推送
- 健康检查
- 集群同步:设计到数据同步,数据同步我们有哪些协议 raft 、distro、ZAB
三、Nacos作为注册中心源码分析
1、项目准备
-
客户端项目:msbshop-parent 注意版本的对应
-
服务端项目:nacos-2.1.0
下载对应的nacos,进行编译
mvn clean install -DskipTests -Drat.skip=true -f pom.xml
-
启动源码服务时候指定参数
2、服务启动入口
2.1 整体流程图
2.2 源码分析
通过学习Nacos1.4 我们知道了我们的注册对应的类是NacosNamingService,那我们Nacos2.1.0是不是也是一样的呢?
通过上图我们发现里面也有注册的信息,那我们打上断点看一下是否到这里,我们在对应注册方法打上断点,然后debug启动,如图 :
我们发现服务启动后他确实通过这里,所以这里是注入的入口,那他是从哪里来这里的呢?
我们想nacos是和Springboot整合,那可能使用了SpringBoot的自动装配
我们查看我们引入的Jar包spring-cloud-starter-alibaba-nacos-discovery.jar 查看里面spring.factoris文件
我们可以查看自动装配类,在这里我们可以通过名称来推断那个关于注册自动配置类,NacosServiceRegistryAutoConfiguration应该和我们注册相关,我们进入看一下,导入以下类
我们可以通过上面debug端点看一下堆栈信息,看涉及到那个类
他的默认实现类是NacosAutoServiceRegistration
NacosAutoServiceRegistration 实现了ApplicationListener接口,监听事件WebServerInitializedEvent(spring核心方法refresh的完成后广播事件)
2.3 总结
找入口的方式:自动装配类 spring.factories
事件驱动:NacosAutoServiceRegistration实现了applicationListener接口
3、实例注册
3.1 整体流程图
3.2 客户端
3.2.1 源码分析
我们的初始化代码如下:
上面是通过实例的参数ephemeral值来判断是否是grpcClientProxy还是httpClientProxy,我们在他的实例化的位置能判断ephemeral的值
我们根据堆栈信息去看找到instance实例化的位置
具体实例创建
分析NacosDiscoveryProperties
由此我们知道这里使用grpc客户端端和服务端通信,同时我们能得出结论:我们
grpcClientProxy的调用
3.2.2 源码总结
- 判断变量 1、 debug 2、 全文搜索 定位赋值位置
- 通过ephemeral的值判断是grpc通信,还是http通信,通过这我们能判断ap模式是用的grpc模式,cp模式是用http通信
- 判断服务端处理类的方式,我们可以根据请求参数,找对应服务端的处理类(由于开源框架都是规范的,一般都是根据请求参数来命名,所以可以采用这种方式)
3.3 服务端
3.3.1源码分析
这里我们注意我们实际注册的应该是对应的实例,而不是服务,服务包括多个实例,具体上的实例才有对应的ip和port
问题:这里一个服务对应一个实例,我们知道一个服务应该对应多个实例,为什么这里对应一个实例呢? 他是怎样处理的?后面我们进行解答
对事件ClientRegisterServiceEvent的监听,我们可以通过全文搜索来看,哪里对应的处理的
下面进行事件监听进行处理:
publisherIndexs就是注册表
3.3.2 源码总结
- 分清实例和服务的关系: 我们实际注册的是实例 ,一个服务包含多个实例
- 这里注册实例会注册到我们Client中,Client有个Map,key:是service value是对应的一个实例,也就是一个Client对应一个服务的具体实例
- 我们发送事件后来处理注册表,注册表结构是ConcurrentMap<Service, Set<String>> publisherIndexes 里面key:是服务,value对应Client的clientId
4、注册表
4.1 注册表结构分析
下面是我们nacos2.1.0的注册表
这里是Nacos1.4x的注册表
4.2 总结
通过Nacos1.4 和Nacos2.1 版本的注册表结构会发现,1.4 比较复杂,而2.1是比较简单,这样2.1的加锁的地方要少一些,所以2.1的注册表的结构性能要好,所以说我们可以总结一下,Nacoa2.1比Nacos1.4性能好的原因:
- Nacos2.1 使用的是GRPC性能要好一些
- Nacos2.1 表结构简单,所以加锁的地方要少一些,性能更好一些
5、服务发现
5.1 客户端
5.1.1 源码分析
我们需要从客户端找到服务发现的入口,我们注册的入口类是NacosNamingService,那我们服务发现入口应该也在这里:我们看一下有两个方法:getAllInstances和selectInstances
那具体是那个方法,我们可以在每个方法里面打上断点,然后debug启动后,进行访问
发送请求
从上图可以判断他是通过selectInstances来获取数据的
我们通过堆栈信息我们能发现,他的调用路径是通过ribbon最终调用selectInstances方法。
key1: 从缓存中获取数据
这里注意,他是从缓存中获取数据,那他一定有定时任务处理这里的数据,否则他就会有脏数据的问题,带着这个问题我们继续学习。
key2:进行订阅
我们服务启动第一次一定是空的,所以我们进行订阅,当定于的clientProxy,具体是那个值呢?
我们能确定clientProxy的具体实现类是NamingClientProxyDelegate,好我们看一下具体
key1:定时任务
这里的UpdateTask一定是个Runable,所以我们看他的run方法
总结上图内容:从缓存获取,如果没有则发送请求获取,如果有则判断数据是否超时,在finally里面对应的内容是这个任务每6秒执行一次,如果失败就会扩大两倍时长,最大是一分钟
里面更新缓存中内容如下:
这里先写内存后写磁盘,那磁盘数据什么时候获取呢?
我们在ServiceInfoHolder构造方法中发现?
那我们看一下服务发现对应代码
5.1.2 源码总结
- 服务启动时候读取磁盘中数据放到缓存
- 读取磁盘数据 如果没有则发送请求获取数据,然后写到缓存
- 读取磁盘数据,如果有则判断时间是否过期,过期则发送请求,写到缓存
- 读取数据是一个定时任务每6秒执行一次,如果失败就会延长,但最大时长是1分钟
5.2 服务端
5.2.1 源码分析
从上文中我们可以分析出我们服务端处理类应该是ServiceQueryRequestHandler
从上图可知getAllInstancesFromIndex是重点,它是获取我们对应的实例,来我们重点分析一下:
1、获取对应的clientId,这一定是在我们的注册表中获取的,注册表示个Map ,key:是服务名称,value:是对应client的set ,不理解的可以参考一下我们的注册表
2、获取对应实例这里我们详细看一下
这里可以和我们注册表中内容详细看一下
5.2.2 源码总结
这里主要是从注册表中获取数据,所以理解这里需要看一下我们的注册表中,各个数据之间的关系
6、服务订阅
6.1 客户端源码
源码可以参考我们服务发现对应的源码
6.2 服务端源码
6.2.1源码分析
key3: 查询服务列表的信息中,会调用serviceStorage.getData(service) 来获取服务的实例,这个和我们服务发现的服务端源码是一样的,这里就不在重复
key4:进行订阅
key2:把订阅者和服务进行关系绑定
这里和注册相同,每个客户端对应一个Map,Map里面key:是服务 value:就是这个服务对应的实例
key3:发送订阅事件, 我们全文搜索ClientSubscribeServiceEvent 查看处理事件位置
更新订阅表
6.2.2 源码总结
我们的订阅很简单首先是获取对应订阅这个服务的实例,用于返回,然后进行订阅,订阅的信息是我们对应发起定于服务者和被订阅服务会以map形式放到我们的client,然后发送一个事件,这个时间就是更新我们的订阅注册表
7、服务变更推送
这里注意我们推送有两种一种是服务变更推送,一种是订阅推送
服务变更推送:对于一个服务来说,订阅者有好多,我们在订阅表中能看到ConcurrentMap<Service, Set<String>> subscriberIndexes,能获所有订阅者,然后进行推送
订阅者推送:此时服务已经稳定,我这里增加一个msb-order,那对于msb-stock又增加一个订阅者,此时我们应该将msb-stock对应的实例推送给具体新增的实例
7.1 订阅推送
7.2.1服务端源码分析
客户端在订阅的时候发送事件更改注册表后,会再发送一个事件,这个事件就会获取对应的服务实例,然后通知订阅者获取对应的服务的实例,这里和上面我们直接订阅返回对应数据有点重复,这里我们可以注意一下,接下来我们看一下源码。
全文搜索ServiceSubscribedEvent查看处理事件的地方
我们进入addTask方法
我们想这里先从map中获取如果有则合并,没有则放进去,那我们想一定有个地方从这里来获取这个任务来处理;
我们看一下这个类的构造方法
这里有个定时任务来处理我们看一下
我们进入处理任务
这里的processor.process的处理方法
我们分析一下上面到底是那个方法,1、我们可以通过debug 2、我们可以猜测,我们想我们处理的任务是PushDelayTask ,所以我们用它PushDelayTask来搜索一下
所以处理类应该是PushDelayTaskProcessor
PushExecuteTask是实现Runable的线程,那重点应该是他的run方法
进入对应的run方法
key1:获取订阅这个服务的客户端ID
上面是判断是否是全部推送,如果我们有指定的ClientId就不全部推送,如果没有我们就全部推送,我们这是订阅推送,所以有要推送的客户端
key2:进行任务处理
7.2.2 客户端源码分析
服务端发送请求参数是ServerRequest,那我们怎样在客户端找到对应的处理方法呢? 还是老办法,用ServerRequest在客户端进行搜索
这个ServerRequestHandler是个接口,我们找他的实现类,如下
那具体的实现类,我们推理可以知道他一定是NamingPushRequestHandler
好我们来分析一下NamingPushRequestHandler
7.2.3 总结
我们新增服务订阅在更新完注册表会发布一个事件ServiceSubscribedEvent,NamingSubscriberServiceV2Impl.会监听事件,然后把订阅到的数据进行推送过去,这期间真正的工作任务是PushExecuteTask他是一个线程,同时注意这里是订阅,所以我们推送的时候,直接推送给新增订阅者就可以
7.2 服务变更推送
服务变更推送和订阅推送是一样的只是推送的对象不同,我们订阅推送是给新增订阅者进行推送,服务变更推送是给所有订阅这个服务的推送
8、心跳机制
8.1 源码分析
我们进入ConnectionManager里面的start方法中,这个方法上有@PostConstruct,也就是构造方法执行完毕就执行这个方法
上图中我们可以知道它里面线程是每3秒执行一次:
由于里面方法比较多,这里我们可以看一些关键的点:
key5:注销
发布事件ClientDisconnectEvent
8.2 总结
、
9、数据同步
9.1 源码分析
我们在实例注册的时候就应该进行集群同步
全文搜索ClientChangedEvent查看哪里处理
我们进入addTask方法
进入NacosDelayTaskExecuteEngine的构造方法里面,启动一个定时任务ProcessRunnable
查看ProcessRunnable的run方法
我们处理的任务是DistroDelayTask ,所以说我们查看具体方法如下
DistroSyncChangeTask是具体的任务我看一下他的run方法
处理集群同步
从上图中我们知道集群同步的请求是DistroDataRequest, 那我可以在服务端全文搜索
处理数据同步
新增实例
9.2 总结
这里集群同步时候,我们采用的异步处理,这里和我们服务变更推送类似
10、GRPC源码分析
10.1、客户端源码分析
我们找到客户端注册的代码:
通过他初始化实例,我们知道他他真正的调用方法
通过方法getExecuteClientProxy需要确定具体代理类 grpcClientProxy
我们查找grpcClientProxy的初始化的地方。
那NamingGrpcClientProxy应该grpc的核心逻辑
我们进入start方法:
上面方法分为两部分:
key1:是将处理的Handler放到对应的List<ServerRequestHandler> serverRequestHandlers 那后面一定是在客户端处理请求的时候,从哪这里面拿到对应的Handler进行处理
key2: 启动这里 这里是关键,我们进来看一下:
上图是简单描述:我们可以看一下第4、5、6、7步
key4:选择一个服务
key5:连接服务
key6: 发送事件到队列, 我们可以全文搜索哪里处理这个队列
线程池里面的一个线程正在处理,如
key7:
如果前面同步连接都失败的话,我们就进行异步连接
我们全文搜索就会发现在上文中的第二个线程中会进行处理
总结:在连接成功还是连接失败后都是异步进行处理,我们可以参考这种方式
10.2、服务端源码分析
服务处理rpc的请求,那我们可以进行猜测一下服务端进行搜索RpcServer,如下
那BaseRpcServer应该是处理RPC的请求,我们看一下对应代码
通过名称我们应该知道他是启动server,那我们查看哪里调用他
通过上图我们知道,他是构造方法之后我们进行服务启动
key1: 增加拦截器 ,这里主要获取一些远端Ip+port信息
key2: 这里面重点是建立连接和处理请求
key2.1 处理请求
我们想我们Springmvc处理请求的时候,我们是根据路径判断对应的controller,这里我们的请求应该是那个具体的handler呢? 我们是根据type,这个type其实就是请求类型
如下图:获取对应type ,然后根据type获取对应的的RequestHandler
我们进入getByRequestType
从上图我们知道这里是将相应handler以map的形式进行存放,那这个key我们通过debug知道他对应的值,为请求参数的的名称
那什么时候进行初始化的:
那Handler对应泛型的第一参数类型名称是什么,那我们拿InstanceRequestHandler来举例:也就是InstanceRequest
总结:我们处理实例就是用的监听器来获取对应的所有实例,然后以map处理,所有请求从这map中拿取对应的对象。
四、Nacos作为配置中心源码分析
1、什么是Naocs配置中心
官方文档: https://github.com/alibaba/spring-cloud-alibaba/wiki/Nacos-config
Nacos 提供用于存储配置和其他元数据的 key/value 存储,为分布式系统中的外部化配置提供服务器端和客户端支持。使用 Spring Cloud Alibaba Nacos Config,您可以在 Nacos Server 集中管理你 Spring Cloud 应用的外部属性配置。
2、Nacos的使用
2.1 给Nacos2.1.0配置数据库
倒入数据
修改内容
2.2 版本推荐
https://github.com/alibaba/spring-cloud-alibaba/wiki/%E7%89%88%E6%9C%AC%E8%AF%B4%E6%98%8E
2.3 父工程指定版本
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.3.2.RELEASE</version>
</parent>
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
<spring-cloud-alibaba.version>2.2.9.RELEASE</spring-cloud-alibaba.version>
</properties>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-alibaba-dependencies</artifactId>
<version>${spring-cloud-alibaba.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
2.4 子工程引入依赖
<dependencies>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<!-- 单元测试类 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
2.5 利用接口对配置进行操作
public class ConfigListenerTest {
private static String serverAddr = "localhost";
private static String dataId = "nacos-demo.yaml";
private static String group = "DEFAULT_GROUP";
private static ConfigService configService;
@Test
public void testListener() throws NacosException, InterruptedException {
Properties properties = new Properties();
properties.put(PropertyKeyConst.SERVER_ADDR, serverAddr);
//获取配置服务
configService = NacosFactory.createConfigService(properties);;
//获取配置
String content = configService.getConfig(dataId, group, 5000);
System.out.println(content);
//注册监听器
CountDownLatch countDownLatch = new CountDownLatch(5);
configService.addListener(dataId, group, new Listener() {
@Override
public Executor getExecutor() {
return null;
}
@Override
public void receiveConfigInfo(String configInfo) {
System.out.println("配置发生变化:" + configInfo);
countDownLatch.countDown();
}
});
countDownLatch.await();
}
@Test
public void publishConfig() throws NacosException {
Properties properties = new Properties();
properties.put(PropertyKeyConst.SERVER_ADDR, serverAddr);
//获取配置服务
configService = NacosFactory.createConfigService(properties);
configService.publishConfig(dataId,group,"age: 30", ConfigType.PROPERTIES.getType());
}
@Test
public void removeConfig() throws NacosException, InterruptedException {
Properties properties = new Properties();
properties.put(PropertyKeyConst.SERVER_ADDR, serverAddr);
//获取配置服务
configService = NacosFactory.createConfigService(properties);
boolean isRemoveOk = configService.removeConfig(dataId, group);
System.out.println(isRemoveOk);
}
}
2.6 和Springboot整合
@RefreshScope
@RestController
public class NacosConfigController {
@Value("${name}")
private String name;
@RequestMapping("/getName")
public String getName(HttpServletRequest httpRequest){
return name;
}
}
2.7 里面放置定时任务
启动
@EnableScheduling
@SpringBootApplication
public class NacosConfigApplication {
public static void main(String[] args) {
SpringApplication.run(NacosConfigApplication.class);
}
}
增加定时任务
@RestController
public class NacosConfigController {
@Value("${name}")
private String name;
@RequestMapping("/getName")
public String getName(HttpServletRequest httpRequest){
return name;
}
// 定时任务每5秒执行一次
@Scheduled(cron = "*/2 * * * * ?")
public void execute(){
System.out.println("获取姓名:" + name);
}
}
发现问题:如果我们更改配置,则对应的定时任务就失败
2.8 分析失败原因
2.8.1 Schedule执行原理
当我们更改配置的时候,我们看日志会进行容器的刷新
此时容器中并没有对应的NacosConfigController对应的实例对象。所以我们的定时任务不会执行,我们可以调用一下我们controller对应的方法,然后容器中就有了NacosConfigController对应的实例,有了这个实例,我们的定时任务就会执行,因为这个定时任务是基于后置处理器进行执行的。
2.8.2 @RefreshScope对象被清理的原因
那为什么刷新容器后NacosConfigController这Bean都没有了呢?
因为在我们更新配置后,我们的容器会将@RefreshScope标注的对象清掉,好我们来分析一下
Scope对应的有一个接口
真正实例创建 除了单例、多例、其他
我们分析这里从缓存中获取,
我们知道 单例获取是从单例对象池中,原型是重新构建Bean ,而我们Refresh是从BeanLifecycleWraperCache里面
也就是从缓存中获取对象,同时这里有个destroy
总结一下:
1、@RefreshScope中有个@Scope里面值是Refresh,他创建对象是放到对应的缓存中,我们通过GenericScope#get方法从缓存中获取对应Bean对象
2、我们更新数据的时候,会发送一个RefreshEvent事件,容器会监听这个事件,然后将缓存中数据进行删除
3、而我们定时任务是在创建bean的后置处理器中执行的,此时bean都被清理了,所以定时任务也没有了
4、我们再次访问对应的NacosConfigController的时候,我们就会创建对应的对象放到缓存,此时定时任务也就执行了
解决问题方案:
我们缓存删除是监听RefreshEvent事件而处理的,我们现在也可以监听事件进行处理,监听事件,如果事件发生,它回调用对应监听器,然后就会实例化,这样定时任务也可以执行
@Slf4j
@RefreshScope
@RestController
public class NacosConfigController implements ApplicationListener<RefreshScopeRefreshedEvent> {
@Value("${name}")
private String name;
@RequestMapping("/getName")
public String getName(HttpServletRequest httpRequest){
return name;
}
// 定时任务每5秒执行一次
@Scheduled(cron = "*/2 * * * * ?")
public void execute(){
System.out.println("获取姓名:" + name);
}
@Override
public void onApplicationEvent(RefreshScopeRefreshedEvent event) {
log.info("监听刷新容器事件");
}
}
3、源码分析
3.1 服务启动加载bootstrap.propertis
准备环境加载bootstrap.propertis,这里他会发送事件进行监听,我们可以直接进入 #load我们可以在load打上断点
总结:我们可以根据堆栈信息来发现,它是在准备环境的时候发送一个事件,ConfigFileApplicationListener监听事件,最后调用PropertiesPropertySourceLoader 对资源进行加载
3.2 客户端拉取远程配置进行合并
这里最终会调用用到NacosPropertySourceLocator# ,我们在locate上打上断点可以看一下堆栈信息
好,我们看一下locate方法
记载配置文件后最终会用composite进行合并,那他们无论加载共享配置、扩展配置和当前应用配置最终会调到NacosPropertySourceLocator#loadNacosDatalfPresent
重点我们看一下他的配置加载
key1:从本地获取配置
key2:远程获取配置
总结:这里我们注意Spring的扩展点之一:在我们Bean构建之前加载一些数据,比如配置属性,我们就可以用这个扩展点,这里加载配置中心内容,这个内容用于后面bean对象创建。
例如:
进行配置
3.3 服务端处理配置拉取
客户端请求为ConfigQueryRequest,则从服务端进行搜索找到对应处理类
下面对应的方法比较长,我们可以看一下关键的点
总结:分析我们发送请求是从缓存文件中获取到的,这里带出两个问题,1、因为我们是从缓存中获取的,那我们直接修改数据库应该是不起作用的 2、缓存一定是从数据库中获取的,那什么时候设置进去的呢
3.4 服务启动进行数据库数据加载
服务端启动时就会依赖DumpService的init方法,从数据库中load 配置存储在本地磁盘上,并将一些重要的元信息例如MD5值缓存在内存中。服务端会根据心跳文件中保存的最后一次心跳时间,来判断到底是从数据库dump全量配置数据还是部分增量配置数据(如果机器上次心跳间隔是6h以内的话)。
全量dump当然先清空磁盘缓存,然后根据主键ID每次捞取一千条配置刷进磁盘和内存。增量dump就是捞取最近六小时的新增配置(包括更新的和删除的),先按照这批数据刷新一遍内存和文件,再根据内存里所有的数据全量去比对一遍数据库,如果有改变的再同步一次,相比于全量dump的话会减少一定的数据库IO和磁盘I0次数。
构建bean时候一定初始化@PostConstruct 对应的方法
判断全量和增量获取数据
全量拉取
总结:这里的md5值的我们学习,我们md5算法获取对应文件的值,如果这个值变化说明文件发生了变化,利用这中方式我们可以通过文件md5值来判断其他是否有变化
3.5 加载数据发生变更,发送事件
如果队列写满则如下操作:但是我们上面是服务启动暂时没有新的服务进来,所以这里subscriber是空的也就无法调用
如果正常情况下应该是放到队列里面,那就应该有取的地方,全文搜索对应的queue
哪里调用到这里呢?
从这里我们可以知道DefaultPublisher是一个线程,所有会调用run方法
总结:我们应该学会发布监听事件
3.6 监听配置变更
全文搜索ApplicationReadyEvent,查看NacosContextRefresher
发送RefreshEvent事件后,就是对@RefreshScope标志的实例进行删除,这里可以参考2.7分析失败原因
key1:销毁对应实例
key2:发送事件RefreshScopeRefreshedEvent 这也是我们通过监听这个事件,来实例化对应的实例的
3.7 服务端端更改配置
我们配置中心发布配置,一定是调用SpringMVC中的Controller方法 ,我们进行全文搜索,通过名称进行分析应该是ConfigController
key1: 更新数据库
key2:发布事件,进行客户端通知配置变更,以及集群同步
全文搜索ConfigDataChangeEvent
AsyncRpcTask是一个任务,并向里面传递了rpcQuene的一个任务队列,我看一下他是怎样处理的。
key1:我们看一下数据持久化, key2中集群数据同步就是发送一个rpc请求
我们重点key1:
上面我们解释过这里是异步处理一定有个地方处理他
发布LocalDataChangeEvent事件
全文搜索对应的事件
查看任务RpcPushTask对应的方法:
向客户端发送请求,进行配置变更的通知
3.8 客户端处理事件
我们知道发送请求的是ConfigChangeNotifyRequest,好,我们到客户端全文搜索一下,找对应的处理
我们全文搜索listenExecutebell找对应的处理位置
3.9 客户端定时拉取配置
3.10 于Nacos1.x长轮询做对比