
Nacos源码系列—关于服务端那些事儿
作者 |牧小农
来源 | 牧小农(ID:java-mxn)
前言
在上节课中,我们讲解了客户端注册服务的大体流程,客户端在注册服务的时候调用的是 NamingService.registerInstance 来完成实例的注册,在最后呢我们知道服务注册是通过 nacos/v1/ns/instance 接口来完成注册的,我们今天来讲解服务端的注册,首先就从这个接口地址开始,来看具体服务端都做了哪些事情
服务注册
上面是我们从官网中找到的Nacos架构图,从这个图中我们大体可以得出我们要找的接口应该是在NamingService这个服务中,同时我们在项目结构中也可以看到naming这个模块,naming就是实现服务注册的,我们都知道请求路径都是通过controller来进行处理的,而在其中我们可以看到一个InstanceController的这么一个类,那么注册实例肯定会和它有关。可以看到InstanceController类的请求路由即是我们POST请求的路由的部分,如下:
所以,我们就从开始研究接收请求处理服务注册的源码,我们找到通过RestFul API接口,请求类型为Post,的方法,符合条件的只有InstanceController.register方法,这个方法用来接收用户的请求,并且把收到的信息进行解析,装换成实例信息,然后通过getInstanceOperator().registerInstance进行调用,这个方法也是服务注册的核心
我们先来看一下下面这个核心方法
getInstanceOperator().registerInstance(namespaceId, serviceName, instance);
getInstanceOperator() 这个判断是否走Grpc协议,默认走Grpc,所以我们使用的是instanceServiceV2这个实例对象
instanceServiceV2就是InstanceOperatorClientImpl,方法所以我们需要进入的是下面这个实例的处理类
具体方法如下所示:
从Nacos2.0以后,新增了Client模型,管理与该客户机有关的数据内容,如果一个客户机发布了一个服务,那么这个客户机发布的所有服务和订阅者信息都会被更新到一个Client对象中,这个Client对象对应于这个客户机的链接,然后通过事件机制触发索引信息的更新。Client负责管理一个客户机的服务实例注册Publish和服务订阅Subscribe,可以方便地对需要推送的服务范围进行快速聚合,同时一个客户端gRPC长连接对应一个Client,每个Client有自己唯一的 clientId
知道了Client模型后,我们来接着从clientOperationService.registerInstance(service, instance, clientId);找到对应的具体实现
EphemeralClientOperationServiceImpl.registerInstance()
下面这个方法是具体来负责处理服务注册,我们来详细了解一下:
ServiceManager.getInstance().getSingleton() 当调用getSingleton的时候会负责管理service的单例,在这里service会重写equlas和hasCode方法作为key
service中 equal和hasCode方法,namespace+group+name在服务端是一个单例Service
clientManager.getClient() 这里对应的实现类为ConnectionBasedClientManager这个实现类负责管理长连接clientId和client模型的映射关系
client.addServiceInstance(); 抽象类为AbstractClient:负责存储当前客户端服务注册表,也就是 service和instance的关系。
ClientOperationEvent.ClientRegisterServiceEvent() :这里目的是为了过滤目标服务得到最终instance列表建立service和client的关系,能够方便我们快速查询,同时会触发ClientServiceIndexesManager的监听事件。
请求流程图:
服务端监控检查
Nacos作为注册中心不止提供了服务注册和服务发现的功能,还提供了服务可用性检测的功能,在1.0的版本中,临时实例走的是distro协议,客户端向注册中心发送心跳来维持自身的健康(healthy)状态,持久实例则走的是Raft协议存储。
两种检测机制:
- 客户端主动上报机制
- 服务器端主动下探机制
客户端主动上报机制:你主动找上级,说你没有打卡(不健康状态)
服务器端主动下探机制:上级检测到你有不打卡的记录,主动来找你
对于Nacos健康检测机制,我们不能主动去设置,但是健康检查机制是和Nacos的服务实例类型强相关,主要是有两种服务实例:
- 临时实例:客户端主动上报
- 持久实例:服务端主动下探
客户端主动上报
临时实例每隔5秒会主动上报自己的健康状态,发送心跳,如果发送心跳的间隔时间超过15秒,Nacos服务器端会将服务标记为亚健康状态,如果超过30S没有发送心跳,那么服务实例会被从服务列表中剔除
在2.0版本以后,持久实例不变,临时实例而是通过长连接来判断实例是否健康。
长连接: 一个连接上可以连续发送多数据包,在连接保持期间,如果没有数据包发送,需要双方发链路检测包,在Nacos2.0之后,使用Grpc协议代替了http协议。长连接会保持客户端和服务端发送的状态,在源码中ConnectionManager 管理所有客户端的长连接
ConnectionManager: 每3秒检测所有超过20S内没有发生过通讯的客户端,向客户端发起ClientDetectionRequest探测请求,如果客户端在1s内成功响应,则检测通过,否则执行unregister方法移除Connection
如果客户端持续和服务端进行通讯,服务端是不需要主动下探的,只有当客户端没有一直和服务端通信的时候,服务端才会主动下探操作
当服务端操作移除事件以后,会操作notifyClientDisConnected()方法,主要调用的是 clientConnectionEventListener.clientDisConnected(connection)方法,将连接信息传入进去
clientConnectionEventListenerd的实现类是ConnectionBasedClientManager,在这里面会出发清除索引缓存等操作
总结
到这里Nacos服务端的基础的源码就讲完了,有些地方我们没有展开来讲,在后续的源码讲解中,会给大家详细的进行讲解,今天主要讲解了,服务端注册以及监控检查的基础代码,后面会有最新的内容呈现给大家。
