Nacos 入门篇---服务端如何处理客户端的服务注册请求?(三)

一、引言

 ok呀,上个章节我们讲了Nacos客户端的服务自动注册,今天我们来看看服务端接收到了客户端的服务注册请求,服务端都做了哪些事情~

二、目录

目录

一、引言

二、目录

三、回顾上节内容:

四、Nacos 服务代码入口分析​​​​​​​

五、Nacos 服务端服务注册源码分析

六、本章小结


三、回顾上节内容:

     上个章节我们从 Nacos 客户端源码开始讲解,从 Nacos Discover 依赖中,发现了spring.factories创建了相关的配置类,其中就包括了注册类NacosServiceRegistryAutoConfiguration ,这个配置类创建了三个bean对象。其中有一个bean对象实现了监听事件的方法。当Spring容器启动的时候,就会发布一个事件。这个bean对象就会监听到,从而执行真正的注册方法,执行的时候会发送一个健康检查延时任务,告诉Nacos服务端我这个服务还活着,健康检查延时任务每5s执行一次。

四、Nacos 服务代码入口分析

主线任务:找到注册中心模块代码的入口

我们打开Nacos 1.4.1 源码项目,看到了这么多模块,是不是有点懵逼,不知道怎么下手~

这个时候,我们就可以从Nacos的架构图来入手了。注册中心在架构图中叫 naming Server,那我们就在项目中找到 naming 的项目模块。


我们打开项目目录结构一看,这不纯纯就是Springboot项目嘛。我们要找接口肯定就在Controller的包下。

  注册实例的接口地址为:/nacos/v1/ns/instance,一猜就是InstanceController类。再看下RequestMapping当中的请求地址,常量拼接起来:v1/ns/instance。正好跟我们要找的请求地址相对应,那代码就是在这个类当中了。

五、Nacos 服务端服务注册源码分析

主线任务:Nacos服务端接受到了实例注册请求,它做了什么事 ?

    我们先看接受请求的代码,很简单。先从request当中获取参数,然后调用serviceManager.registerInstance() 方法。

@CanDistro
@PostMapping
@Secured(parser = NamingResourceParser.class, action = ActionTypes.WRITE)
public String register(HttpServletRequest request) throws Exception {// 接受客户端的参数// 从request当中获取 namespaceId(命名空间ID)、serviceName(服务名称)、Instance实例final String namespaceId = WebUtils.optional(request, CommonParams.NAMESPACE_ID, Constants.DEFAULT_NAMESPACE_ID);final String serviceName = WebUtils.required(request, CommonParams.SERVICE_NAME);NamingUtils.checkServiceNameFormat(serviceName);// Instance 里面就包含了客户端的 ip、port 等信息final Instance instance = parseInstance(request);// 上面代码就是获取参数// 主线任务:调用服务注册的实现类serviceManager.registerInstance(namespaceId, serviceName, instance);return "ok";
}

紧接着看serviceManager类中的registerInstance方法。因为我们第一次看,抓住主线任务代码看,分支代码后面可以再去细看。

public void registerInstance(String namespaceId, String serviceName, Instance instance) throws NacosException {// 不知道是创建了一个什么服务createEmptyService(namespaceId, serviceName, instance.isEphemeral());// 根据namespaceId、serviceName获取 Service服务Service service = getService(namespaceId, serviceName);// service为空就抛出异常if (service == null) {throw new NacosException(NacosException.INVALID_PARAM,"service not found, namespace: " + namespaceId + ", service: " + serviceName);}// 上面都是分支代码// 主线任务:添加服务实例addInstance(namespaceId, serviceName, instance.isEphemeral(), instance);
}

然后调用了 ServiceManager当中的 addInstance方法,这里key还是比较重要的,这个后面去讲一下。

public void addInstance(String namespaceId, String serviceName, boolean ephemeral, Instance... ips)throws NacosException {// 根据namespaceId、serviceName、ephemeral,获取一个Key,在代码最后put进去// 重点:后面再去补充一下这个Key的生成规则String key = KeyBuilder.buildInstanceListKey(namespaceId, serviceName, ephemeral);// 根据namespaceId、serviceName,获取 Service服务Service service = getService(namespaceId, serviceName);// 锁住一个servicesynchronized (service) {// 这里提前说一下,ips 上层方法传过来的,是本次实例注册对应的Instance,也就是已开始从Request里面获取的参数信息。// 最后会放在instanList里面,为什么这里是List,说明它不仅仅只有一个,还会包含之前已经注册的Instance,放在了一个List里面List<Instance> instanceList = addIpAddresses(service, ephemeral, ips);// 创建一个 Instances 对象,并且把 instanceList 属性set进去Instances instances = new Instances();instances.setInstanceList(instanceList);// 主线任务:调用了consistencyService.put 方法,把key和Instances对象当作参数传进去consistencyService.put(key, instances);}
}

然后接着往 consistencyService.put() 方法点击调用时,发现了有好几个类都实现该接口。怎么办 ?

两种办法:

  • 第一种:Debug的方式,一步一步进去看,就知道对应哪个类了。
  • 第二种:看这个属性是怎么注入的。

这里的话,我们采用第二种方法。如下代码,我们能看到这个对象来自consistencyDelegate,注入的时候已经指定对应的Bean了,Bean的名字肯定是唯一的。这里我们用IDEA快捷键跳转,一下子就能找到了。

然后接着调用DelegateConsistencyServiceImpl实现类当中的最后一步put方法了,根据不同的key选择不同对象进行put方法。

@Override
public void put(String key, Record value) throws NacosException {// 先通过key选择不同的 Service,然后调用对应 Service 的 put 方法mapConsistencyService(key).put(key, value);
}private ConsistencyService  mapConsistencyService(String key) {return KeyBuilder.matchEphemeralKey(key) ? ephemeralConsistencyService : persistentConsistencyService;
}

那这个 Record 参数是什么 ?

其实就是 Instances 对象实现了 Record 的接口,Instances 对象当中有个instanceList属性,这个属性包含了之前已经注册的实例和新需要注册的实例。

private List<Instance> instanceList = new ArrayList<>();

那这个 key 是怎么来的 ?生成规则是什么 ?(这里重点说明一下,不然都不知道方法接着该怎么走了)这个key的生成是在前面 addInstance() 的方法当中

// namespaceId:命名空间id , serviceName: 服务名称,ephemeral:是否为临时实例
String key = KeyBuilder.buildInstanceListKey(namespaceId, serviceName, ephemeral);

点进 KeyBuilder.buildInstanceListKey() 方法当中看,是根据 ephemeral 这个参数来生成不同的key。

ephemeral 这个参数看着有点眼熟,之前在讲 Nacos客户端 发送心跳的时候遇到过,根据 ephemeral 这个参数判断是否发送健康心跳。这个参数默认是为:true

那在服务端这个 ephemeral 参数是什么意思呢?我第一次看也有点懵逼,这个时候还是得看官方Open API文档。

public static String buildInstanceListKey(String namespaceId, String serviceName, boolean ephemeral) {// 根据 ephemeral 来生成不同的 key// ephemeral:是否为临时实例 默认为:truereturn ephemeral ? buildEphemeralInstanceListKey(namespaceId, serviceName): buildPersistentInstanceListKey(namespaceId, serviceName);
}

看到文档上的说明,ephemeral:是否临时实例

那我们就可以得知,Naocs 客户端默认注册的实例就是临时实例。那到底什么是临时实例,这个就和AP、CP架构有关系了,这个后面看集群源码的时候再去说明,现在你只要知道,Nacos 默认注册的实例都是临时实例。

ok,那我们接着看 key的生成规则,既然 ephemeral 为true的话,走的是 buildEphemeralInstanceListKey(namespaceId, serviceName) 方法。

那拼接起来key的格式为:com.alibaba.nacos.naming.iplist.ephemeral. +namespaceId + ## + serviceName

private static String buildEphemeralInstanceListKey(String namespaceId, String serviceName) {// com.alibaba.nacos.naming.iplist.ephemeral. +namespaceId + ## + serviceNamereturn INSTANCE_LIST_KEY_PREFIX + EPHEMERAL_KEY_PREFIX + namespaceId + NAMESPACE_KEY_CONNECTOR + serviceName;
}

那我们现在知道了 key 的大概生成规则是什么样了~

我们接着看 DelegateConsistencyServiceImpl 当中的mapConsistencyService方法。

那 key 是默认为 com.alibaba.nacos.naming.iplist.ephemeral. +namespaceId + ## + serviceName格式的,所以这里应该返回 ephemeralConsistencyService对象。

private ConsistencyService  mapConsistencyService(String key) {// 判断根据不同的key选择不同的对象进行返回return KeyBuilder.matchEphemeralKey(key) ? ephemeralConsistencyService : persistentConsistencyService;
}public static boolean matchEphemeralKey(String key) {// currently only instance list has ephemeral type:return matchEphemeralInstanceListKey(key);
}// 判断key包含com.alibaba.nacos.naming.iplist.ephemeral开头.就为true,返回 ephemeralConsistencyService 对象
public static boolean matchEphemeralInstanceListKey(String key) {return key.startsWith(INSTANCE_LIST_KEY_PREFIX + EPHEMERAL_KEY_PREFIX);
}

那我们就着往 ephemeralConsistencyService 对象当中的put方法看。这个 Record 参数前面已经说过了。直接看核心代码 onPut() 方法。

@Override
public void put(String key, Record value) throws NacosException {// 核心代码onPut(key, value);// 集群节点同步,这个后面文章中讲解distroProtocol.sync(new DistroKey(key, KeyBuilder.INSTANCE_LIST_KEY_PREFIX), DataOperation.CHANGE,globalConfig.getTaskDispatchPeriod() / 2);
}

onPut() 方法 创建Datum对象,把key、和 Instances都放入Datum对象里面去,最后调用了 dataStore.put 方法。点进去,接着往下看,如下图:

public void onPut(String key, Record value) {// 这里还是判断刚刚那个 key 前缀,这里是为 trueif (KeyBuilder.matchEphemeralInstanceListKey(key)) {// 创建Datum对象,把key、和 Instances都放入Datum对象里面去Datum<Instances> datum = new Datum<>();datum.value = (Instances) value;datum.key = key;datum.timestamp.incrementAndGet();// 最后添加到dataStore当中,这个 dataStore就是一个Map对象dataStore.put(key, datum);}if (!listeners.containsKey(key)) {return;}// 主线任务:添加任务notifier.addTask(key, DataOperation.CHANGE);
}

DataStore 里面有个Map方法,put 方法也就是把刚刚创建的 Datum 当作 value 放了进去。key就是前面生成的key,value包含了 key 以及 instances 两部分。

最后我们来看下 addTask 方法,datumKey 还是前面 addInstance 方法生成的key,action 是 DataOperation.CHANGE。最后把把key、action包装成 Pair 对象,放入到阻塞队列当中就结束了。

private BlockingQueue<Pair<String, DataOperation>> tasks = new ArrayBlockingQueue<>(1024 * 1024);/*** 向队列中添加新的通知任务。* @param datumKey data key* @param action   action for data*/
public void addTask(String datumKey, DataOperation action) {if (services.containsKey(datumKey) && action == DataOperation.CHANGE) {return;}if (action == DataOperation.CHANGE) {services.put(datumKey, StringUtils.EMPTY);}// 主线任务:taskks 是一个阻塞队列,并且把key、action包装成 Pair 对象,放入队列当中tasks.offer(Pair.with(datumKey, action));
}

看完了代码思路不够清晰,这是本章节的分析图,可以根据分析图再过下代码:

六、本章小结

主线任务:Nacos服务端接受到了实例注册请求,它做了什么事 ?

通过源码分析可以得知,先从 request 请求当中接受从客户端传过来的参数。在 addInstance 方法当中生成了一个 key。根据调用注入接口bean得知,调用到了ConsistencyService接口下的DelegateConsistencyServiceImpl实现类当中的put方法。然后又根据不同 key 判断选择调用不同 Service。

最终默认调用到了 EphemeralConsistencyService 接口下的DistroConsistencyServiceImpl当中put方法,又在put方法当中调用了 onPut 方法,在onPut 方法当中,创建了Datum对象,把key、和 Instances都放入Datum对象里面去。放入了DataStore中的Map里。这个步骤有什么用?后面分析源码在进行说明

最后调用了addTask 方法,把key、action包装成 Pair 对象,放入阻塞队列当中。

你以为放入到阻塞队列就完了吗 ?答案:肯定不是,这个后面我们在进行分析,大家可以猜一猜后面都做了什么操作

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/815958.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

工作流引擎项目解析(二)

流程变量设置于获取 Activiti--流程变量_runtimeservice.setvariable-CSDN博客 Process Variables | docs.camunda.org 设置 启动时设置任务完成时设置runtimeService.setvars…()taskservice.setvars…() ProcessEngine processEngine ProcessEngines.getDefaultProcessEn…

python项目练习——24、线旅游预订系统

用户管理功能: 用户注册:用户可以填写用户名和密码进行注册。用户登录:注册过的用户可以使用用户名和密码登录系统。用户资料管理:用户可以查看和编辑自己的个人资料。旅游产品管理功能: 产品列表展示:展示可预订的旅游产品列表。产品搜索:用户可以根据关键字、目的地、…

Nginx 基础应用实战 04 在公网配置配置HTTPS

Nginx 基础应用实战 04 在公网配置配置HTTPS Nginx配置 server {listen 443 ssl;server_name aa.abc.com;ssl_certificate /data/cert/server.crt;ssl_certificate_key /data/cert/server.key;}免费签名 https://freessl.cn 阿里云 腾讯云 Nginx配置 serve…

Springboot整合nacos报错无法连接nacos

报错信息&#xff1a;Nacos com.alibaba.nacos.api.exception.NacosException: Client not connected, current status:STARTING 关于这个报错的原因有很多&#xff1a;如Nacos未启动、网络问题、配置问题、版本不兼容问题等&#xff0c;我的报错原因主要是版本不兼容。 下面…

5.9 mybatis之callSettersOnNulls作用

文章目录 1. 当callSettersOnNullstrue时2. 当callSettersOnNullsfalse时 在mybatis的settings配置参数中有个callSettersOnNulls参数&#xff0c;官方解释为&#xff1a;指定当结果集中值为 null 的时候是否调用映射对象的 setter&#xff08;map 对象时为 put&#xff09;方法…

PostgreSQL入门到实战-第三十弹

PostgreSQL入门到实战 PostgreSQL教程网站官网地址PostgreSQL概述更新计划 PostgreSQL教程网站 https://www.postgresqltutorial.com/ 官网地址 声明: 由于操作系统, 版本更新等原因, 文章所列内容不一定100%复现, 还要以官方信息为准 https://www.postgresql.org/PostgreS…

【系统分析师】计算机网络

文章目录 1、TCP/IP协议族1.1 DHCP协议1.2 DNS协议1.3网络故障诊断 2、网路规划与设计2.1逻辑网络设计2.2物理网络设计2.3 分层设计 3、网络接入3.1 接入方式3.2 IPv6地址 4、综合布线技术5、物联网5.1物联网概念与分层5.2 物联网关键技术 6、云计算7、网络存储技术&#xff08…

neo4j使用详解(结尾、neo4j的java driver使用模板及工具类——<可用于生产>)

Neo4j系列导航: neo4j安装及简单实践 cypher语法基础 cypher插入语法 cypher插入语法 cypher查询语法 cypher通用语法 cypher函数语法 neo4j索引及调优 neo4j java Driver等更多 1. 简介 本文主要是java使用neo4j driver操作neo4j的模板项目及非常有用的工具类,主要包括: 图…

Vector - CAPL - XCP介绍_02

前面我们介绍了关于使用vector XCP License后&#xff0c;通过CAPL对XCP协议进行连接、断开和获取当前XCP连接状态的函数&#xff0c;本篇文章不做过多的其他赘述&#xff0c;我们继续介绍CAPL控制XCP相关的其他函数。 目录 xcpActivate 代码示例 xcpDeactivate xcpActiva…

数据库-Redis(9)

目录 41.AOF重写日志时,有新数据写入怎么办呢? 42.主线程Fork出子进程是如何复制内存数据的?

LeetCode617:合并二叉树

题目描述 给你两棵二叉树&#xff1a; root1 和 root2 。 想象一下&#xff0c;当你将其中一棵覆盖到另一棵之上时&#xff0c;两棵树上的一些节点将会重叠&#xff08;而另一些不会&#xff09;。你需要将这两棵树合并成一棵新二叉树。合并的规则是&#xff1a;如果两个节点重…

8:系统开发基础--8.1:软件工程概述、8.2:软件开发方法 、8.3:软件开发模型、8.4:系统分析

转上一节&#xff1a; http://t.csdnimg.cn/G7lfmhttp://t.csdnimg.cn/G7lfm 课程内容提要&#xff1a; 8&#xff1a;知识点考点详解 8.1&#xff1a;软件工程概述 1.软件的生存周期 2.软件过程改进—CMM Capability Maturity Model能力成熟度模型 3.软件过程改进—CMMI—…

万字长文:Phoenix面试题及参考答案(持续更新)

目录 描述一下Phoenix的架构及其组件。 解释Phoenix如何处理SQL查询并将其转换为HBase操作。

2024.4.14

#define _CRT_SECURE_NO_WARNINGS #include<stdio.h> #include<stdlib.h> #include<stdbool.h>/*/*给你一个整数数组 nums 。 如果一组数字(i, j) 满足 nums[i] nums[j] 且 i < j &#xff0c;就可以认为这是一组 好数对 。 返回好数对的数目。int numId…

vmware安装win10及ubuntu

安装win10 新建一个文件夹 选择刚才创建的文件夹 选择需要保存文件的位置&#xff0c;还是选择刚才创建的文件夹 选择自定义硬件 选择下载的win10镜像iso文件,导入后&#xff0c;点击完成即可 接下来就是下一步 没有此电脑&#xff0c;可以点击个性化-》主题-》桌面设置…

SecureCRT日志记录的7个经典配置记录与14个环境变量(%Y-%M-%D_%H_%S_session.log %t )

每次更换电脑、主机或者环境都需要配置一遍SecureCRT的参数。感觉就最近十年都已经设置过上百次了。其实设置没什么特别的&#xff0c;只是经过不断地打磨&#xff0c;主打的就是一个经济实用。经常忘记&#xff0c;特此记录。 配置方式 建议直接配置默认session&#xff1a;…

Linux journalctl命令详解

文章目录 1.介紹2.概念设置system time基本的日志查阅方法按时过滤日志&#xff08;by Time&#xff09;显示本次启动以来的日志&#xff08;Current Boot&#xff09;按Past Boots按时间窗口按感兴趣的消息筛选按unit按进程、用户、Group ID按组件路径显示内核消息按消息优先级…

Bash 编程精粹:从新手到高手的全面指南之逻辑控制

在 Unix 和 Linux 系统中&#xff0c;Bash&#xff08;Bourne-Again Shell&#xff09;是一种广泛使用的 shell&#xff0c;提供了强大的脚本编程能力。本文将详细介绍 Bash 脚本中的逻辑控制结构&#xff0c;包括条件判断、分支选择、循环控制以及退出控制等内容。 条件判断&…

Centos安装MySQL提示公钥尚未安装

一、问题 在Centos7.9使用yum安装MySQL时出现错误&#xff0c;提示&#xff1a;mysql-community-server-5.7.44-1.el7.x86_64.rpm 的公钥尚未安装&#xff0c;如下图所示&#xff1a; 执行命令&#xff1a;systemctl start mysqld也提示错误&#xff1a;Failed to start mysq…

Spark-机器学习(1)什么是机器学习与MLlib算法库的认识

从这一系列开始&#xff0c;我会带着大家一起了解我们的机器学习&#xff0c;了解我们spark机器学习中的MLIib算法库&#xff0c;知道它大概的模型&#xff0c;熟悉并认识它。同时&#xff0c;本篇文章为个人spark免费专栏的系列文章&#xff0c;有兴趣的可以收藏关注一下&…