前言
前面写过几篇Nacos的服务注册文章,感觉还差点什么,那么这不就来了么,Nacos服务发现剖析!Nacos服务发现不像Nacos服务注册和心跳,Nacos服务注册和心跳不涉及到其他第三方技术,但是Nacos服务发现涉及到Ribbon和Feign,本文突出重点不讲Ribbon和Feign关于这部分后期会在对应领域分享出来,Nacos服务发现核心接口为以下
这里跳过Ribbon和Feign相关技术,直接切入重点!
Nacos-服务发现源码主流程
Nacos-服务发现源码分析
updateListOfServers方法
该方法位于DynamicServerListLoadBalancer类中,而这个并不是Nacos包中的
而是位于Ribbon中的,实际上Nacos服务发现启动的入口是由Ribbon牵引启动的!连接Nacos部分的是下面这行代码
servers = serverListImpl.getUpdatedListOfServers();
面向接口编程的体现,这里Nacos客户端包中有实现ServerList接口,那么这里就连接上了Nacos相关部分
切入getUpdatedListOfServers方法
深入selectInstances方法
进入getServiceInfo方法
这里算的上是Nacos客户端服务发现的心脏部分,简单先说明一下,
方框1:部分是获取本地缓存的服务节点
方框2:部分是当本地不存在当前远程调用的服务节点信息时向Nacos服务端获取服务列表
方框3:部分是在多线程情况下做自旋锁,自旋等待时长为5秒
方框4:部分是开启延迟任务10秒向Nacos服务端获取最新的服务列表
那么接下来就深入这4个部分详细看看各部分实现
方块1部分
这部分是这4块逻辑中最简单的,就是从内存Map中获取服务信息
方块2部分
这部分就相对其他部分复杂点,而且这部分关联到方框3中的逻辑,(凌晨两点了,抽根烟提提神)我们接着逐行解读!首先当缓冲中不存在我们远程调度的服务时,那么进入这个逻辑!
serviceObj = new ServiceInfo(serviceName, clusters);
先构建ServiceInfo部分信息!
updatingMap.put(serviceName, new Object());
这行代码其实就是为方块3部分加锁,防止高并发问题!这部分设计个人觉得非常巧妙!大大的一个赞!
updateServiceNow(serviceName, clusters);
这行代码算的上是整个核心的核心了!这个 **updateServiceNow
**方法在方块4中有使用,特别重要。进入看看!
String result = serverProxy.queryList(serviceName, clusters, pushReceiver.getUDPPort(), false);
这行代码就是向Nacos发起Http请求获取要调度客户端服务列表的!进去看看
返回的信息如下
然后拿到调度服务列表后下一步就是processServiceJSON(result);方法!
if (StringUtils.isNotEmpty(result)) {
processServiceJSON(result);
}
当结果不为空时将字符串还原为ServiceInfo,并将从Nacos获取到的节点列表跟新到serviceInfoMap缓存中!也就是方块1部分的缓存!
然后我们回到方块2主体部分没接着updateServiceNow方法调用完的逻辑!
updatingMap.remove(serviceName);
这里就是将锁删除,为什么这里要加锁呢,这个我们在方块三中分析!
方块3部分
这其实是个自旋锁!,因为方块2中要发起Http请求,这个耗时是不去确定的,这里设置5秒的自旋时间,防止并发冲突!
方块4部分
看到这里我们就非常能确定这里是个延迟任务了,这里延迟时间是10秒,
if (futureMap.get(ServiceInfo.getKey(serviceName, clusters)) != null) {
return;
}
而且这里是为每个服务名+集群名作为一个任务维度创建定时任务的!我们看看定时执行的任务!
这里又是调用updateServiceNow方法这个方法和方块2中的逻辑一样,都是从Nacos中获取调度的客户端节点服务列表,并且写入本地缓存中!这里的逻辑有点像客户端心跳的延迟任务一样,这里我有点想不清楚,为什么要延迟任务中调度延迟任务,为什么不是直接开启定时任务或者周期任务,可能这里是考虑每个任务的耗时长短不一样吧!那么这个方块4中的逻辑就是每个10秒从Nacos中获取调度节点服务列表信息,并跟新本地服务列表!我们回到主流程!
回到HostReactor的getServiceInfo方法
那么这里就会从本地调度节点服务列表中返回服务列表!
继续往上返回到NacosNamingService的selectInstances方法
回到NacosServerList的getServers
那么这里从Nacos中获取服务列表的逻辑就到这里,再往上,那么就回到了Ribbon部分
回到我们爱情开始的地方
在这里Nacos部分就与我们分手了,剩下的路就要交给Ribbon去走了,加油!奥利给!!!
接下文Ribbon原码分析!!!