SpringCloud之Nacos2.X作为注册中心

目录

1、作为Nacos2.X版本

2、设计注册中心

2-1、分布式框架的注意点:三高架构

2-2、注册中心的设计

3、Nacos作为注册中心源码分析

3-1、项目准备

3-2、服务启动入口

3-2-1、整体流程图

3-2-2、源码分析

3-2-3、总结

3-3、实例注册

3-3-1、整体流程图

3-3-2、客户端

3-3-2-1、源码分析

3-3-2-2、源码总结

3-3-3、服务端

3-3-3-1、源码分析

3-3-3-2、源码总结

3-4、注册表

3-4-1、注册表结构分析

3-4-2、总结

3-5、服务发现

3-5-1、客户端

3-5-1-1、源码分析

3-5-1-2、源码总结 

3-5-2、服务端

3-5-2-1、源码分析

3-5-2-2、源码总结

3-6、服务订阅

3-6-1、客户端源码

3-6-2、服务端源码

3-6-2-1、源码分析

3-6-2-2、源码总结

3-7、服务变更推送

3-7-1、订阅推送

3-7-1-1、服务端源码分析

3-7-1-2、客户端源码分析

3-7-1-3、总结

3-7-2、服务变更推送

3-8、心跳机制

3-8-1、源码分析

3-8-2、总结

3-9、数据同步

3-9-1、源码分析

3-9-2、总结

3-10、GRPC源码分析

3-10-1、客户端源码分析

3-10-2、服务端源码分析


1、作为Nacos2.X版本

Spring Cloud Alibaba 新版本中Seata 1.5.2和Nacos 2.1.0 在性能和使用方面都有很大提升,这节课将从使用和源码的角度详细讲解这两个框架。

2、设计注册中心

2-1、分布式框架的注意点:三高架构

  • 高可用

    高可用性(High Availability)通常来描述一个系统经过专门的设计,从而减少停工时间,而保持其服务的高度可用性(一直都能用)。

    解决方案:集群

  • 高并发

    高并发(High Concurrency)是互联网分布式系统架构设计中必须考虑的因素之一,它通常是指,通过设计保证系统能够同时并行处理很多请求。 高并发相关常用的一些指标有响应时间(Response Time),吞吐量(Throughput),每秒查询率QPS(Query Per Second),并发用户数等。

    解决方案:

    • 垂直扩展增强单机硬件性能

    • 水平扩展

  • 高性能

    高性能(High Performance)就是指程序处理速度快,所占内存少,cpu占用率低

    解决方案:

    指程序处理速度快: 这里设计我们数据存储结构、访问机制、集群同步

2-2、注册中心的设计

  • 服务注册

  • 注册表结构设计

  • 服务发现

  • 服务订阅

  • 服务推送

  • 健康检查

  • 集群同步:设计到数据同步,数据同步我们有哪些协议 raft 、distro、ZAB

3、Nacos作为注册中心源码分析

3-1、项目准备

  • 客户端项目:msbshop-parent 注意版本的对应

  • 服务端项目:nacos-2.1.0,下载对应的nacos,进行编译
  • mvn clean install -DskipTests -Drat.skip=true -f pom.xml
  • 启动源码服务时候指定参数

3-2、服务启动入口

3-2-1、整体流程图

3-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的完成后广播事件)

3-2-3、总结

  • 找入口的方式:自动装配类 spring.factories
  • 事件驱动:NacosAutoServiceRegistration实现了applicationListener接口

3-3、实例注册

3-3-1、整体流程图

3-3-2、客户端

3-3-2-1、源码分析

初始化代码如下: 

  • 上面是通过实例的参数ephemeral值来判断是否是grpcClientProxy还是httpClientProxy,在他的实例化的位置能判断ephemeral的值
  • 根据堆栈信息去看找到instance实例化的位置

具体实例创建: 

分析NacosDiscoveryProperties

由此可以知道这里使用grpc客户端端和服务端通信,同时我们能得出结论:我们

grpcClientProxy的调用

3-3-2-2、源码总结
  • 判断变量 1、 debug 2、 全文搜索 定位赋值位置

  • 通过ephemeral的值判断是grpc通信,还是http通信,通过这我们能判断ap模式是用的grpc模式,cp模式是用http通信

  • 判断服务端处理类的方式,我们可以根据请求参数,找对应服务端的处理类(由于开源框架都是规范的,一般都是根据请求参数来命名,所以可以采用这种方式)

3-3-3、服务端

3-3-3-1、源码分析

这里注意实际注册的应该是对应的实例,而不是服务,服务包括多个实例,具体上的实例才有对应的ip和port

问题:这里一个服务对应一个实例,我们知道一个服务应该对应多个实例,为什么这里对应一个实例呢? 它是怎样处理的?后面进行解答

对事件ClientRegisterServiceEvent的监听,我们可以通过全文搜索来看,哪里对应的处理的

publisherIndexs就是注册表

3-3-3-2、源码总结
  • 分清实例和服务的关系: 我们实际注册的是实例 ,一个服务包含多个实例
  • 这里注册实例会注册到我们Client中,Client有个Map,key:是service value是对应的一个实例,也就是一个Client对应一个服务的具体实例
  • 我们发送事件后来处理注册表,注册表结构是ConcurrentMap<Service, Set<String>> publisherIndexes 里面key:是服务,value对应Client的clientId

3-4、注册表

3-4-1、注册表结构分析

下面是我们nacos2.1.0的注册表

这里是Nacos1.4x的注册表

3-4-2、总结

通过Nacos1.4 和Nacos2.1 版本的注册表结构会发现,1.4 比较复杂,而2.1是比较简单,这样2.1的加锁的地方要少一些,所以2.1的注册表的结构性能要好,所以说我们可以总结一下,Nacoa2.1比Nacos1.4性能好的原因:

  1. Nacos2.1 使用的是GRPC性能要好一些

  2. Nacos2.1 表结构简单,所以加锁的地方要少一些,性能更好一些

3-5、服务发现

3-5-1、客户端

3-5-1-1、源码分析

我们需要从客户端找到服务发现的入口,我们注册的入口类是NacosNamingService,那我们服务发现入口应该也在这里:我们看一下有两个方法:getAllInstances和selectInstances

那具体是那个方法,我们可以在每个方法里面打上断点,然后debug启动后,进行访问

发送请求

从上图可以判断他是通过selectInstances来获取数据的

我们通过堆栈信息我们能发现,他的调用路径是通过ribbon最终调用selectInstances方法。

key1: 从缓存中获取数据

  • 这里注意,它是从缓存中获取数据,那它一定有定时任务处理这里的数据,否则它就会有脏数据的问题,带着这个问题继续。
  • key2:进行订阅
  • 我们服务启动第一次一定是空的,所以我们进行订阅,当定于的clientProxy,具体是那个值呢?

能确定clientProxy的具体实现类是NamingClientProxyDelegate

key1:定时任务

这里的UpdateTask一定是个Runable,所以我们看他的run方法 

总结上图内容:从缓存获取,如果没有则发送请求获取,如果有则判断数据是否超时,在finally里面对应的内容是这个任务每6秒执行一次,如果失败就会扩大两倍时长,最大是一分钟

里面更新缓存中内容如下:

这里先写内存后写磁盘,那磁盘数据什么时候获取呢?

我们在ServiceInfoHolder构造方法中发现?

看一下服务发现对应代码

3-5-1-2、源码总结 
  • 服务启动时候读取磁盘中数据放到缓存

  • 读取磁盘数据 如果没有则发送请求获取数据,然后写到缓存

  • 读取磁盘数据,如果有则判断时间是否过期,过期则发送请求,写到缓存

  • 读取数据是一个定时任务每6秒执行一次,如果失败就会延长,但最大时长是1分钟

3-5-2、服务端

3-5-2-1、源码分析

从上文中我们可以分析出我们服务端处理类应该是ServiceQueryRequestHandler

从上图可知getAllInstancesFromIndex是重点,它是获取我们对应的实例,来我们重点分析一下:

 1、获取对应的clientId,这一定是在我们的注册表中获取的,注册表示个Map ,key:是服务名称,value:是对应client的set ,不理解的可以参考一下我们的注册表

2、获取对应实例这里我们详细看一下

这里可以和我们注册表中内容详细看一下

3-5-2-2、源码总结

这里主要是从注册表中获取数据,所以理解这里需要看一下我们的注册表中,各个数据之间的关系

3-6、服务订阅

3-6-1、客户端源码

源码可以参考我们服务发现对应的源码

3-6-2、服务端源码

3-6-2-1、源码分析

key3: 查询服务列表的信息中,会调用serviceStorage.getData(service) 来获取服务的实例,这个和我们服务发现的服务端源码是一样的,这里就不在重复

key4:进行订阅

 

key2:把订阅者和服务进行关系绑定

这里和注册相同,每个客户端对应一个Map,Map里面key:是服务 value:就是这个服务对应的实例

key3:发送订阅事件, 我们全文搜索ClientSubscribeServiceEvent 查看处理事件位置

更新订阅表

3-6-2-2、源码总结

我们的订阅很简单首先是获取对应订阅这个服务的实例,用于返回,然后进行订阅,订阅的信息是我们对应发起定于服务者和被订阅服务会以map形式放到我们的client,然后发送一个事件,这个时间就是更新我们的订阅注册表

3-7、服务变更推送

  • 这里注意我们推送有两种一种是服务变更推送,一种是订阅推送
  • 服务变更推送:对于一个服务来说,订阅者有好多,我们在订阅表中能看到ConcurrentMap<Service, Set<String>> subscriberIndexes,能获所有订阅者,然后进行推送
  • 订阅者推送:此时服务已经稳定,我这里增加一个msb-order,那对于msb-stock又增加一个订阅者,此时我们应该将msb-stock对应的实例推送给具体新增的实例

3-7-1、订阅推送

3-7-1-1、服务端源码分析

客户端在订阅的时候发送事件更改注册表后,会再发送一个事件,这个事件就会获取对应的服务实例,然后通知订阅者获取对应的服务的实例,这里和上面我们直接订阅返回对应数据有点重复,这里我们可以注意一下,接下来我们看一下源码。

全文搜索ServiceSubscribedEvent查看处理事件的地方

我们进入addTask方法

  • 我们想这里先从map中获取如果有则合并,没有则放进去,那我们想一定有个地方从这里来获取这个任务来处理;
  • 我们看一下这个类的构造方法

这里有个定时任务来处理我们看一下

 我们进入处理任务

这里的processor.process的处理方法

我们分析一下上面到底是那个方法,1、我们可以通过debug 2、我们可以猜测,我们想我们处理的任务是PushDelayTask ,所以我们用它PushDelayTask来搜索一下

所以处理类应该是PushDelayTaskProcessor

PushExecuteTask是实现Runable的线程,那重点应该是他的run方法

进入对应的run方法

 key1:获取订阅这个服务的客户端ID

上面是判断是否是全部推送,如果我们有指定的ClientId就不全部推送,如果没有我们就全部推送,我们这是订阅推送,所以有要推送的客户端

key2:进行任务处理

3-7-1-2、客户端源码分析

服务端发送请求参数是ServerRequest,那我们怎样在客户端找到对应的处理方法呢? 还是老办法,用ServerRequest在客户端进行搜索

这个ServerRequestHandler是个接口,我们找他的实现类,如下

那具体的实现类,我们推理可以知道他一定是NamingPushRequestHandler

好我们来分析一下NamingPushRequestHandler

3-7-1-3、总结

我们新增服务订阅在更新完注册表会发布一个事件ServiceSubscribedEvent,NamingSubscriberServiceV2Impl.会监听事件,然后把订阅到的数据进行推送过去,这期间真正的工作任务是PushExecuteTask他是一个线程,同时注意这里是订阅,所以我们推送的时候,直接推送给新增订阅者就可以

3-7-2、服务变更推送

服务变更推送和订阅推送是一样的只是推送的对象不同,我们订阅推送是给新增订阅者进行推送,服务变更推送是给所有订阅这个服务的推送

3-8、心跳机制

3-8-1、源码分析

我们进入ConnectionManager里面的start方法中,这个方法上有@PostConstruct,也就是构造方法执行完毕就执行这个方法

上图中我们可以知道它里面线程是每3秒执行一次:

由于里面方法比较多,这里我们可以看一些关键的点:

key5:注销

发布事件ClientDisconnectEvent

3-8-2、总结

3-9、数据同步

3-9-1、源码分析

我们在实例注册的时候就应该进行集群同步

全文搜索ClientChangedEvent查看哪里处理

我们进入addTask方法

进入NacosDelayTaskExecuteEngine的构造方法里面,启动一个定时任务ProcessRunnable

查看ProcessRunnable的run方法

我们处理的任务是DistroDelayTask ,所以说我们查看具体方法如下

DistroSyncChangeTask是具体的任务我看一下他的run方法

处理集群同步

从上图中我们知道集群同步的请求是DistroDataRequest, 那我可以在服务端全文搜索

处理数据同步

新增实例

3-9-2、总结

这里集群同步时候,我们采用的异步处理,这里和我们服务变更推送类似

3-10、GRPC源码分析

3-10-1、客户端源码分析

我们找到客户端注册的代码:

通过他初始化实例,我们知道他他真正的调用方法

通过方法getExecuteClientProxy需要确定具体代理类 grpcClientProxy

我们查找grpcClientProxy的初始化的地方。

那NamingGrpcClientProxy应该grpc的核心逻辑

 我们进入start方法:

上面方法分为两部分:

key1:是将处理的Handler放到对应的List<ServerRequestHandler> serverRequestHandlers 那后面一定是在客户端处理请求的时候,从哪这里面拿到对应的Handler进行处理

key2: 启动这里 这里是关键,我们进来看一下:

 上图是简单描述:我们可以看一下第4、5、6、7步

key4:选择一个服务

key5:连接服务

key6: 发送事件到队列, 我们可以全文搜索哪里处理这个队列

线程池里面的一个线程正在处理,如

如果前面同步连接都失败的话,我们就进行异步连接

我们全文搜索就会发现在上文中的第二个线程中会进行处理

总结:在连接成功还是连接失败后都是异步进行处理,我们可以参考这种方式

3-10-2、服务端源码分析

服务处理rpc的请求,那我们可以进行猜测一下服务端进行搜索RpcServer,如下

那BaseRpcServer应该是处理RPC的请求,我们看一下对应代码

通过名称我们应该知道他是启动server,那我们查看哪里调用他

 

通过上图我们知道,他是构造方法之后我们进行服务启动

key1: 增加拦截器 ,这里主要获取一些远端Ip+port信息

key2: 这里面重点是建立连接和处理请求

key2.1 处理请求

我们想我们Springmvc处理请求的时候,我们是根据路径判断对应的controller,这里我们的请求应该是那个具体的handler呢? 我们是根据type,这个type其实就是请求类型

如下图:获取对应type ,然后根据type获取对应的的RequestHandler

我们进入getByRequestType

那Handler对应泛型的第一参数类型名称是什么,那我们拿InstanceRequestHandler来举例:也就是InstanceRequest

总结:我们处理实例就是用的监听器来获取对应的所有实例,然后以map处理,所有请求从这map中拿取对应的对象。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值