当前位置: 首页 > 科技观察

说说NacosClient服务发现源码分析

时间:2023-03-19 23:04:56 科技观察

本文转载自微信公众号《程序新视野》,作者为二哥。转载本文请联系程序新视界公众号。学习不需要那么功利,二哥带你从更高的维度轻松阅读源码~本文将带你从源码层面分析NacosClient的服务发现之旅,事实未必像你想象的那样简单。Nacos服务发现直观上,Nacos客户端的服务发现就是封装参数,调用服务端接口,获取返回的实例列表。在naocos中,如果细化这个流程,会发现它不仅仅包括通过NamingService获取服务列表,在获取服务列表的过程中还涉及通信协议(Http或gRPC)、订阅流程、故障转移逻辑等服务清单。下面看一下基于服务发现的相关流程。先说入口程序,在NamingTest中依然可以看到:NamingServicenamingService=NacosFactory.createNamingService(properties);namingService.registerInstance("nacos.test.1",instance);ThreadUtils.sleep(5000L);//获取实例列表Listlist=namingService.getAllInstances("nacos.test.1");NamingService的实例化和基本功能在服务注册中已经讲过了。这里直接参考getAllInstances方法获取实例列表。该方法的参数是服务的名称。经过一系列的重载方法调用,真正处理核心逻辑的方法如下:,,",");//订阅方式if(subscribe){//先从客户端缓存获取服务信息serviceInfo=serviceInfoHolder.getServiceInfo(serviceName,groupName,clusterString);if(null==serviceInfo){//如果localcacheisnotSubscribeifserviceInformationserviceInfo=clientProxy.subscribe(serviceName,groupName,clusterString);}}else{//如果没有订阅服务信息,直接从服务端查询serviceInfo=clientProxy.queryInstancesOfService(serviceName,groupName,clusterString,0,false);}//从服务信息中获取实例列表Listlist;if(serviceInfo==null||CollectionUtils.isEmpty(list=serviceInfo.getHosts())){returnnewArrayList();}returnlist;}先看重载的getAllInstances方法,比入口方法??多了几个参数.不仅有服务名,还有组名(groupName)、集群列表(clusters)、是否订阅(subscribe)。重载方法中的其他参数已经设置了默认值。比如组名默认为“DEFAULT_GROUP”,集群列表默认为空数组,是否订阅默认为“subscribe”。上面的方法整理成流程图如下:naocos上面流程的基本逻辑是:如果是订阅模式,直接从本地缓存中获取服务信息(ServiceInfo),然后从中获取实例列表,因为订阅机制会自动将服务器实例的变化同步到本地。如果本地缓存中没有,说明是先调用,再订阅,订阅完成后获取服务信息。如果是非订阅模式,则直接向服务器请求获取服务信息。订阅处理流程在上面的过程中,涉及到了订阅逻辑。入口代码是实例列表中的如下方法:serviceInfo=clientProxy.subscribe(serviceName,groupName,clusterString);我们来看看这个方法内部是如何处理的。首先,这里的clientProxy是NamingClientProxy类的一个对象。对应的订阅实现如下:@OverridepublicServiceInfosubscribe(StringserviceName,StringgroupName,Stringclusters)throwsNacosException{StringserviceNameWithGroup=NamingUtils.getGroupedName(serviceName,groupName);StringserviceKey=ServiceInfo.getKey(serviceNameWithGroup,clusters);//获取ServiceInfoServiceInforesult=serviceInfoHolder.getInfoHolder.getinthecache().get(serviceKey);if(null==result){//如果为null,则进行订阅逻辑处理,基于gRPC协议result=grpcClientProxy.subscribe(serviceName,groupName,clusters);}//定时调度UpdateTaskserviceInfoUpdateService.scheduleUpdateIfAbsent(serviceName,groupName,clusters);//ServiceInfo本地缓存处理serviceInfoHolder.processServiceInfo(result);returnresult;}上面代码中可以看到在获取服务实例列表时(尤其是第一次),也是订阅逻辑扩展,基本流程图如下:从fl可以看出上图在naocos中,订阅方法首先通过代理类判断本地缓存,如果本地缓存中有ServiceInfo信息则直接返回。如果不存在,则默认使用gRPC协议订阅,返回ServiceInfo。grpcClientProxy的subscribe订阅方法直接向服务端发送订阅请求并返回结果,没有做太多处理。订阅完成后,会通过ServiceInfoUpdateService启动定时任务。这个定时任务的主要作用是定时同步服务器端的实例列表信息,进行本地缓存更新等操作。最后一步,ServiceInfo本地缓存处理。这里会将最新获取到的ServiceInfo与本地内存中的ServiceInfo进行比对,进行更新、发布变更时间、磁盘文件存储等操作。这一步的操作其实也是在订阅定时任务中处理的。关于订阅详情和本地缓存处理,涉及的内容比较多,后面会单独展开说明。只知道这里的整个过程。总结本文主要梳理Nacos客户端服务发现的核心流程,包括:首先,如果没有开启订阅模式,直接通过/instance/list接口获取服务实例列表信息(默认通过gRPC协议);其次,如果开启订阅模式(默认开启),会先从本地缓存中获取实例信息,如果不存在则进行订阅,获取实例信息;第三,开启订阅后,会开启定时任务,定时执行UpdateTask(从服务器获取)。实例信息,更新本地缓存,发布事件);第四,第二步获取到最新的实例信息后,还会执行processServiceInfo方法更新内存和本地实例缓存,并发布变更时间。第五,至此,与第二步形成循环。每次获取本地缓存,如果不存在则更新...关于处理订阅相关的UpdateTask和处理本地缓存的ServiceInfoHolder#processServiceInfo方法,我们会在后面的文章中继续讲解.