前言

前面写过几篇Nacos的服务注册文章,感觉还差点什么,那么这不就来了么,Nacos服务发现剖析!Nacos服务发现不像Nacos服务注册和心跳,Nacos服务注册和心跳不涉及到其他第三方技术,但是Nacos服务发现涉及到Ribbon和Feign,本文突出重点不讲Ribbon和Feign关于这部分后期会在对应领域分享出来,Nacos服务发现核心接口为以下
Nacos-服务发现_Nacos
这里跳过Ribbon和Feign相关技术,直接切入重点!

Nacos-服务发现源码主流程

Nacos-服务发现_延迟任务_02

Nacos-服务发现源码分析

updateListOfServers方法
该方法位于DynamicServerListLoadBalancer类中,而这个并不是Nacos包中的
Nacos-服务发现_其他_03

而是位于Ribbon中的,实际上Nacos服务发现启动的入口是由Ribbon牵引启动的!连接Nacos部分的是下面这行代码

servers = serverListImpl.getUpdatedListOfServers();

Nacos-服务发现_其他_04
面向接口编程的体现,这里Nacos客户端包中有实现ServerList接口,那么这里就连接上了Nacos相关部分

切入getUpdatedListOfServers方法
Nacos-服务发现_服务发现_05
深入selectInstances方法
Nacos-服务发现_服务发现_06
进入getServiceInfo方法
Nacos-服务发现_Nacos_07
这里算的上是Nacos客户端服务发现的心脏部分,简单先说明一下,
方框1:部分是获取本地缓存的服务节点
方框2:部分是当本地不存在当前远程调用的服务节点信息时向Nacos服务端获取服务列表
方框3:部分是在多线程情况下做自旋锁,自旋等待时长为5秒
方框4:部分是开启延迟任务10秒向Nacos服务端获取最新的服务列表

那么接下来就深入这4个部分详细看看各部分实现

方块1部分
Nacos-服务发现_服务列表_08
Nacos-服务发现_延迟任务_09
Nacos-服务发现_其他_10
这部分是这4块逻辑中最简单的,就是从内存Map中获取服务信息

方块2部分
Nacos-服务发现_服务列表_11
这部分就相对其他部分复杂点,而且这部分关联到方框3中的逻辑,(凌晨两点了,抽根烟提提神)我们接着逐行解读!首先当缓冲中不存在我们远程调度的服务时,那么进入这个逻辑!

serviceObj = new ServiceInfo(serviceName, clusters);

先构建ServiceInfo部分信息!

updatingMap.put(serviceName, new Object());

这行代码其实就是为方块3部分加锁,防止高并发问题!这部分设计个人觉得非常巧妙!大大的一个赞!

 updateServiceNow(serviceName, clusters);

这行代码算的上是整个核心的核心了!这个 **updateServiceNow**方法在方块4中有使用,特别重要。进入看看!
Nacos-服务发现_其他_12

String result = serverProxy.queryList(serviceName, clusters, pushReceiver.getUDPPort(), false);

这行代码就是向Nacos发起Http请求获取要调度客户端服务列表的!进去看看
Nacos-服务发现_服务发现_13
返回的信息如下
Nacos-服务发现_延迟任务_14
然后拿到调度服务列表后下一步就是processServiceJSON(result);方法!

 if (StringUtils.isNotEmpty(result)) {
                processServiceJSON(result);
            }

当结果不为空时将字符串还原为ServiceInfo,并将从Nacos获取到的节点列表跟新到serviceInfoMap缓存中!也就是方块1部分的缓存!

然后我们回到方块2主体部分没接着updateServiceNow方法调用完的逻辑!

updatingMap.remove(serviceName);

这里就是将锁删除,为什么这里要加锁呢,这个我们在方块三中分析!

方块3部分
Nacos-服务发现_其他_15
这其实是个自旋锁!,因为方块2中要发起Http请求,这个耗时是不去确定的,这里设置5秒的自旋时间,防止并发冲突!
方块4部分
Nacos-服务发现_服务列表_16
Nacos-服务发现_Nacos_17
Nacos-服务发现_延迟任务_18

看到这里我们就非常能确定这里是个延迟任务了,这里延迟时间是10秒,

if (futureMap.get(ServiceInfo.getKey(serviceName, clusters)) != null) {
    return;
}

而且这里是为每个服务名+集群名作为一个任务维度创建定时任务的!我们看看定时执行的任务!
Nacos-服务发现_服务列表_19
这里又是调用updateServiceNow方法这个方法和方块2中的逻辑一样,都是从Nacos中获取调度的客户端节点服务列表,并且写入本地缓存中!这里的逻辑有点像客户端心跳的延迟任务一样,这里我有点想不清楚,为什么要延迟任务中调度延迟任务,为什么不是直接开启定时任务或者周期任务,可能这里是考虑每个任务的耗时长短不一样吧!那么这个方块4中的逻辑就是每个10秒从Nacos中获取调度节点服务列表信息,并跟新本地服务列表!我们回到主流程!

回到HostReactor的getServiceInfo方法
Nacos-服务发现_服务列表_20
那么这里就会从本地调度节点服务列表中返回服务列表!

继续往上返回到NacosNamingService的selectInstances方法
Nacos-服务发现_服务列表_21
回到NacosServerList的getServers
Nacos-服务发现_服务发现_22
那么这里从Nacos中获取服务列表的逻辑就到这里,再往上,那么就回到了Ribbon部分

回到我们爱情开始的地方
Nacos-服务发现_服务发现_23
在这里Nacos部分就与我们分手了,剩下的路就要交给Ribbon去走了,加油!奥利给!!!

接下文Ribbon原码分析!!!