上章节我这边带着大家看了下Nacos的源码,针对上节课做个总结:
- Nacos服务注册过程深度剖析
- Nacos注册表如何防止多节点读写并发冲突
- Nacos高并发支撑异步队列与内存队列剖析
- Nacos心跳机制(讲了一半)
那么本节课我们将继续带着大家往下看几个点
- Nacos服务发现源码深度剖析
- Nacos心跳机制与服务健康检查深度剖析
- Nacos服务变动事件发布源码剖析
1、Nacos服务发现源码深度剖析
对于服务发现,Nacos在Server端提供了一个API,由官网可以看出:
https://nacos.io/zh-cn/docs/open-api.html
Nacos的客户端通过Server端暴露的API,进行拉取服务对应的ip等信息;
还是回到源码来看。
从NacosNamingService里的getAllInstances方法进行重载的执行。
边边角角的代码先不看,if判断像是订阅,getServiceInfo()见明知意,获取服务信息
再看getServiceInfo0();这里像是从一个缓存里去取出
说明某个信息存在一个Map里,看看这个Map的结构
private final Map
serviceInfoMap; 这个很明显,应该就是服务信息;
判断如果从缓存中取的为空,就去做这个操作
updateServiceNow(serviceName, clusters);
去更新缓存,实际上走的还是
/instance/list
看看代码
不管什么情况,最后会走到
scheduleUpdateIfAbsent(serviceName, clusters);
里面是个定时任务UpdateTask,里面实际上是个run方法,定时获取服务端最新的数据更新到本地的任务。
所以到这,服务发现的主线源码,目前就比较清晰了。
2、Nacos心跳机制与服务健康检查深度剖析
上节课讲心跳,讲到了一个类NamingService,核心的地方是添加一个BeatInfo
那BeanInfo是什么呢?构造组装一个心跳的实体,里面存放一些信息,比如当前服务名称+ip+端口+权重+当前属于什么cluster+心跳周期等等。
最终进入addBeanInfo,这里会new一个BeatTask任务,并且把5s作为一个周期去发送心跳。
会调用sendBeat方法去向Server端发送请求,instance/beat
来看server端的这个请求,其他的边线代码不看,看主线serviceManager.registerInstance()
里面是上节课说过的,注册一个instance实例,加入到本地缓存,加入到本地缓存作为服务实例,然后会立刻开启一个任务ClientBeatProcessor,更新客户端实例的最后心跳时间
最终调用instance.setLastBeat,上节说过,会比较当前实例的最后心跳时间和当前时间,如果超过15s无响应,健康状态置为false,超过30s,会下线此实例。
那么客户端是怎么感知服务端的服务是否存在,并及时更新状态或者下线的呢?
当服务第一次注册上的时候,服务端会开启定时任务的检查
并且定时任务是5s执行一次
包括如何删除ip,也在这里面体现。
那么服务端的健康检查任务这里也讲了比较主要的过程。
3、Nacos服务变动事件发布源码剖析
有没有考虑过一个问题,客户端会搞个定时任务定时拉取服务端的注册列表,那这么这个时间我假设是5s拉取一次,如果这个时候有服务下线或者替换,那么必须要等到下一个5s才知道这个服务不存在,这样的话实时性会跟不上,于是Nacos做了个主动推送的动作!
先来回顾下服务发现的过程:
在NacosNamingService.getAllInstance()中,获取客户端的服务实例缓存信息
如果获取的缓存为空,调用server获取最新的数据,updateServiceNow
然后延时执行定时任务,更新客户端的服务缓存scheduleUpdateIfAbsent(),最终进入到finally,定时执行这个任务,failCount默认值是0,Min计算当前延时的时间,和failCount最终的值进行左移运算,并和默认时间*60s做对比,这个过程中我们可以不详细解答,最坏的情况,Default_Delay*60=60s,也就是1min中会定时拉取最新数据缓存到客户端本地(这样的概率太低)。
这样显然可能会存在延时和数据不同步的问题,那么对于Nacos,做了个主动推送的机制。当服务端列表有变动的时候,服务端主动推送一个Udp请求,让客户端自己更新;
在Service.onChange中,updateIPs里面会进行发布事件
全文搜索下这个ServiceChangeEvent
里面有个UpdPush(),上面这么多逻辑代码都是在封装这个ackEntry,里面是一些服务端变化的信息,这个不是主线逻辑,我就不带大家看细节。
最终执行到updSocket.send()
udp相对会不稳定,和tcp不一样,所以网络丢包的时候会受到影响,当然丢了就丢了, 有变化再传一次就可以了,udp是不保证数据传输的稳定性的。那么这个策略,其实大大降低了ap架构中的数据不一致的风险。
总结下:Zookeeper中在进行服务注册的时候,发起一个长连接,比如用Nio或者
Netty,会一直占用管道,而Nacos只是发起一个http请求,发起请求后就结束了,Nacos在
1.4.x版本中是典型的短链接(当然2.0后改用gRpc长连接),而Zk采用长连接方式建立通
道,如果在客户端服务器非常非常多,会比较耗性能的,Nacos相比会轻量不少哦,Zk为了
保证服务变动的一致性,监听回调机制就会立刻通知到客户端,响应是很及时的,所以Zk保
证了Cp,Nacos有两块保证了心跳,一块是客户端的定时拉取,一块是udp反向推送,即便
udp丢失了,也有定时任务兜底。
本节就先讲述到这,下一章讲解Nacos集群架构下的同步源码!