首页 > 其他分享 >Nacos服务端代码分析

Nacos服务端代码分析

时间:2023-06-30 20:14:59浏览次数:148  
标签:服务 service 代码 Nacos 实例 注册表 put 服务端

InstanceController

进入InstanceController类,可以看到一个register方法,就是服务注册的方法了:

点击查看代码
    @CanDistro
    @PostMapping
    @Secured(parser = NamingResourceParser.class, action = ActionTypes.WRITE)
    public String register(HttpServletRequest request) throws Exception {
        //获取namespaceId
        final String namespaceId = WebUtils
                .optional(request, CommonParams.NAMESPACE_ID, Constants.DEFAULT_NAMESPACE_ID);
        //尝试获取serviceName,其格式为 group_name@@service_name
        final String serviceName = WebUtils.required(request, CommonParams.SERVICE_NAME);
        NamingUtils.checkServiceNameFormat(serviceName);
        // 解析出实例信息,封装为Instance对象
        final Instance instance = parseInstance(request);
        // 注册实例
        serviceManager.registerInstance(namespaceId, serviceName, instance);
        return "ok";
    }
这里,进入到了serviceManager.registerInstance()方法中。 ## ServiceManager ServiceManager就是Nacos中管理服务、实例信息的核心API,其中就包含Nacos的服务注册表:


而其中的registerInstance方法就是注册服务实例的方法:

我们可以继续跟进createEmptyService这个方法:

再继续跟进createServiceIfAbsent方法如果缺席就创建,也就是说不存在就创建,第一次来就创建。

进来createServiceIfAbsent以后,首先尝试从注册表中获取一次服务,跟进去

如果不为null则代表有,也就是组Map有,通过这个chooseServiceMap方法得到组map,跟进来:

其实就是从服务列表里面根据命名空间id获取Map。接着通过组的服务名获取Service:

如果为空,说明该服务是第一次注册,创建新的Service:

这个groupName其实是serviceName的一部分是两个@符号隔开的,我们再第一次创建服务的时候传进去的cluster是null,随意上图中的cluster肯定是null,里面的代码也即不会执行。
记下来就是putServiceAndInit,服务创建好就要把其put进来。继续跟进:

第一个方法putService,继续:
首先是判断一下serviceMap注册表是否有这个服务,这里其实是为了提高性能(单例模式就有这种判断),如果没有就设置一把锁,出于线程安全考虑,当我在创建的时候别人就不要创建了,否则出现并发写修改的安全问题,然后在这个同步代码块里面又判断一次有没有,其实就是为了保证线程安全,如果确实没有就往注册表里面put:

但这个ConcurrentSkipListMap并不是在同步代码块里填的,而是出来这个同步代码块,在这个:
serviceMap.get(service.getNamespaceId()).putIfAbsent(service.getName(), service);
这句代码填入的真正的服务,这样确保我们的服务一定能写进去。
其中的putIfAbsent表示:如果不存在才put,如果并发情况下有其他人put进来了,就不往里面put了。这就是保证put动作的有效性。
然后service已经有了,我们取一次,并进行初始化,其实是进行一个健康检测:

接下来进行一致性服务的监听:consistencyService
监听服务的变更:

到这里,putServiceAndInit这个服务注册和初始化就做完了:

但是要记得putServiceAndInit方法里面的两个监听器。后续分析会用到
继续往下执行createEmptyService后,我们需要添加实例到service中addInstance这个方法,跟进:

addInstance(namespaceId, serviceName, instance.isEphemeral(), instance);记住这些参数。
比较重要的是上图的同步代码块

点击查看代码
synchronized (service) {
          // 拷贝注册表中旧的实例列表,然后结合新注册的实例,得到最终的实例列表
          List<Instance> instanceList = addIpAddresses(service, ephemeral, ips);

          //封装实例列表到instances
          Instances instances = new Instances();
          instances.setInstanceList(instanceList);

          //更新注册表(更新本地注册表、同步给nacos集群中的其他节点)
          consistencyService.put(key, instances);
      }
也就是说同一个服务只能有一个线程完成服务注册的动作。因为微服务集群中,假如有成百上千的服务,而这些服务同时来作注册,那一个服务里面往往有多个实例,假如这个服务将来有100个实例,那这一百个实例就有可能并行来执行,如果大家都来修改就有可能造成一些脏写的问题,所以这里要加锁,就是为了在多线程同时操作一个服务资源时为了保证线程安全就要加上同步锁,所以同一个服务的多个实例只能串行注册,避免并发修改。 从上面的同步代码块总共三步: 1.拷贝注册表中旧的实例列表,然后结合新注册的实例,得到最终的实例列表 2.封装实例列表到instances 3.更新注册表(更新本地注册表、同步给nacos集群中的其他节点) 整个过程第三步最耗时:consistencyService.put(key, instances);Ctrl+Alt+鼠标左键put这个方法,可以发现其有很多的实现:


因为ConsistencyService本身是个接口,它有很多的实现,Ctrl+h可以看到如下图:

标签:服务,service,代码,Nacos,实例,注册表,put,服务端
From: https://www.cnblogs.com/wekenyblog/p/17517714.html

相关文章

  • Netty-TCP 03.服务端
    本文是使用Netty开发一个简单的TCP通讯(聊天)应用程序的第【3】部分,主要介绍服务端的实现。模块划分跟客户端类似,服务端也是主要分为三个部分:心跳检测处理消息消费处理TCP服务实现心跳检测服务端需要定时检测客户端是否在线(即是否发送心跳),如果没有,那么将客户端连接断开,同样......
  • [代码]DOM和LINQ to XML创建XML树的三种方式
    此代码主要示范了DOM和LINQtoXML三种创建XML树的方式。第01种、使用W3CDOM创建XML树可以使用XmlDocument.CreateElement()方法创建XML元素。使用XmlElement.InnerText为元素添加内容,比如在元素的开始标记和结束标记之间添加字符串内容。使用XmlElement.SetAttribute()方法为元素......
  • [代码]使用LINQ的查询结果填充XML树(LINQ to XML)
    此代码使用的是LINQtoXML的函数构造功能创建一个XML树,其中树中的一些元素是通过LINQ查询结果填充。实际上,通过本例也可以看到如何使用LINQtoXML将原始XML文档转换成为另外一种XML文档。下面代码中使用的PurchaseOrder.xml文档内容:<?xmlversion="1.0"?><PurchaseOrderPurchas......
  • [代码]创建XDocument对象(LINQ to XML)
    LINQtoXML中的XDocument是用来表示内存中的XML文档的。同样可以使用函数构造功能来创建此对象。此示例代码主要用来演示如何创建一个通用的XML文档。示例代码代码中创建的XDocument对象,包含了2个注释(XComment),1个处理指令(XProcessingInstrucation),1个根元素及其若干子元素,也为......
  • [代码]如何从文件加载XML(LINQ to XML)
    此示例代码演示如何使用XElement.Load()方法从文件中加载XML。示例代码usingSystem;usingSystem.Collections.Generic;usingSystem.Linq;usingSystem.Text;usingSystem.Xml.Linq;namespaceDemo02Ex01{classProgram{staticvoidMain(string[]arg......
  • [代码]如何分析XML字符串(LINQ to XML)
    此代码演示如何使用XElement.Parse()方法分析XML字符串,并创建XElement元素。从编程效率来讲这种做法可能效率非常的高,但从代码的执行效率来看,还是使用函数构造的方式创建XML树的效率高一点。示例代码usingSystem;usingSystem.Collections.Generic;usingSystem.Linq;usingSys......
  • [代码]元素的附加和克隆(LINQ to XML)
    当将一个XElement对象添加给一个XML树的时候,如果此XElement对象没有父级,即不是另外一个XML树的一部分,此时将会将此元素直接添加给XML树;如果XElement对象有父级,则会克隆这个XElement对象,然后将克隆的对象添加到新的XML树。示例代码代码中的Child1被添加到XmlTree1元素,因此它已经有了......
  • 接口签名规则和Java实现签名和验签代码
    接口签名规则和Java实现签名和验签代码签名规则 签名生成的通用步骤如下: 第一步,设所有发送或者接收到的数据为集合M,将集合M内非空参数值的参数按照参数名ASCII码从小到大排序(字典序),使用URL键值对的格式(即key1=value1&key2=value2…)拼接成字符串stringA。 特别注意以下重要规则: ◆......
  • 代码随想录|打家劫舍问题
    198.打家劫舍 213.打家劫舍II  337.打家劫舍III 198.打家劫舍classSolution:defrob(self,nums:List[int])->int:n=len(nums)ifn==0:return0dp=[0for_inrange(n+1)]dp[1]=nums[0]......
  • 一文拆解“复杂软件”的无代码配置逻辑
    最近教研组的小伙伴们收到了一些用户在试用smardaten过程中的困惑。为了解答大家的疑问,今天特别邀请了教研组的美女小姐姐,以一个比较简单易理解的场景“疫情填报”,来拆解一下smardaten如何支撑应用的搭建逻辑,以及smardaten作为企业级无代码与轻量级无代码的配置逻辑差异。课程视频......