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);

    // 锁住一个service
    synchronized (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:是否为临时实例 默认为:true
    return 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 + ## + serviceName
    return 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 前缀,这里是为 true
    if (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 对象,放入阻塞队列当中。

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

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

react antd 实现修改密码(原密码,新密码,再次输入新密码,新密码增加正则复杂度校验)

先看样子 组件代码&#xff1a; import React, { useState, useEffect } from react import { Row, Col, Modal, Spin, Input, Button, message, Form } from antd import { LockOutlined, EyeTwoTone, EyeInvisibleOutlined } from ant-design/icons import * as Serve from …

改进 Elastic Agent 和 Beats 中的事件队列

作者&#xff1a;Fae Charlton, Alexandros Sapranidis 内部改进如何降低 Elastic 8.13 中的内存使用。 在 8.12 版本中&#xff0c;我们引入了性能预设 —— 一种更简单的方法&#xff0c;用于调整 Elastic Agent 和 Beats 以适应各种场景。这提高了常见环境的性能&#xff0…

喜讯|我司再次获得国家发明专利,硬核科技研发成果呈加速度增长

热烈祝贺 璞华软件成功获得国家发明专利 我司自主研发成果《一种工伤案件裁决书的生成方法及装置》&#xff08;专利号&#xff1a;ZL 2019 1 1170975.8&#xff09;成功获得国家发明专利&#xff0c;2024年4月成功获得专利证书。 发明人&#xff1a;高志凯&#xff1b;杨德…

微商商城源码小程序好用么?

商城APP作为电子商务行业的重要组成部分&#xff0c;已经成为了人们购物的主要方式之一。为了在竞争激烈的市场中脱颖而出&#xff0c;开发一款专业且思考深度的商城APP方案显得尤为关键。本文将从专业性和思考深度两个方面&#xff0c;探讨商城APP的开发方案。 一、专业性的重…

BGI | STCellBin:动植物组织细胞分割

简介 STCellbin 利用细胞核染色图像作为桥梁来获取与空间基因表达图谱对齐的细胞膜/壁染色图像。通过采用先进的细胞分割技术&#xff0c;可以获得准确的细胞边界&#xff0c;从而获得更可靠的单细胞空间基因表达谱。此次更新的增强功能为细胞内基因表达的空间组织提供了宝贵的…

uc网盘推广拉新渠道

​近期&#xff0c;UC网盘拉新项目在收益上展现出了一些明显的优势&#xff0c;这些优势对于推广者来说是非常吸引人的。以下是对这些优势的详细分析&#xff1a; 上手容易&#xff1a;与其他项目相比&#xff0c;UC网盘的报备审核过程相对宽松&#xff0c;速度也更快。在快节奏…

Parameter-Efficient Fine-Tuning for Large Models: A Comprehensive Survey

Parameter-Efficient Fine-Tuning for Large Models: A Comprehensive Survey PDF: https://arxiv.org/pdf/2403.14608.pdf 1 概述 大型模型在多个领域取得了显著进展&#xff0c;但它们的大规模参数带来了高昂的计算成本。这些模型需要大量资源来执行&#xff0c;尤其是在针…

【python】__name__函数的用法详解!

上一篇中&#xff0c;说到了__init__函数的使用&#xff0c;__init__函数是在类中实现&#xff0c;它在创建对象时自动执行&#xff0c;用于初始化对象的属性。今天我们来说一下__name__函数&#xff0c;__name__函数的主要作用为&#xff1a; 1.执行python脚本 2.导入到别的…

哪种裤子比较百搭?显高显瘦的男生裤子分享

选到合适的裤子才能穿得好看以及舒服。可是市面上也出现了不少各种裤子质量达不到标准的负面新闻&#xff0c;为了能够选到合适的裤子&#xff0c;我自费购买了多个品牌的裤子测评。之后我知道很多网红品牌为了压低成本&#xff0c;用料和做工都很差&#xff0c;于是我总结了五…

Python和Java哪个更适合后端开发?

Python和Java都是强大的后端开发语言&#xff0c;它们各自有鲜明的特点和适用场景。选择哪一个更适合后端开发&#xff0c;主要取决于具体的项目需求、团队技术栈、个人技能偏好以及长期发展考虑等因素。 下面是两者在后端开发中的优势和劣势&#xff1a; 「Python&#xff1…

TCP/IP协议—TCP

TCP/IP协议—TCP TCP协议TCP通信特点TCP技术概念TCP定时器 TCP头部报文TCP连接三次握手&#xff08;建立连接&#xff09;四次挥手&#xff08;释放连接&#xff09;连接状态 TCP协议 传输控制协议&#xff08;TCP&#xff0c;Transmission Control Protocol&#xff09;是一种…

《柳叶刀》子刊:突破性临床试验,证实粪菌移植治疗帕金森病的潜力

帕金森病&#xff08;Parkinson’s Disease&#xff0c;PD&#xff09;&#xff0c;是一种复杂的神经退行性疾病&#xff0c;也是世界上第二常见的神经退行性疾病&#xff0c;仅次于阿尔茨海默病&#xff08;AD&#xff09;&#xff0c;影响着约1%-2%的65岁及以上老人。随着全球…

Ribbon-负载均衡原理解析(案例)

简介&#xff1a;负载均衡&#xff0c;英文名称为Load Balance&#xff0c;其含义就是指将负载&#xff08;工作任务&#xff09;进行平衡、分摊到多个操作单元上进行运行&#xff0c;例如FTP服务器、Web服务器、企业核心应用服务器和其它主要任务服务器等&#xff0c;从而协同…

明厨亮灶厨师帽佩戴检测的难点与优化方式 Yolov5+bytetrack

随着国家一系列食品安全政策的出台&#xff0c;厨房的安全卫生问题逐渐被人们重视。其中&#xff0c;工作人员是否佩戴厨师帽是很关键的一环。人们希望能通过一种方式实现自动化的检测&#xff0c;但目前市场上大部分“明厨亮灶系统”或“未佩戴厨师帽检测系统”都无法满足用户…

【UE5.1】使用MySQL and MariaDB Integration插件——(1)连接MySQL

效果 步骤 1. 在虚幻商城下载“MySQL and MariaDB Integration”插件 2. 购买安装后&#xff0c;我们将插件添加到一个新工程中&#xff0c;打开新工程可以看到已经添加了插件 3. 新建一个蓝图&#xff0c;选择父类为“MySQLDBConnectionActor” 这里命名为该蓝图为“BP_MySQL…

腾讯测试岗位的面试经历与经验分享【一面、二面与三面】

腾讯两个月的实习一转眼就结束了,回想起当时面试的经过,感觉自己是跌跌撞撞就这么过了,多少有点侥幸.马上腾讯又要来校招了,对于有意愿想投腾讯测试岗位的同学们,写了一些那时候面试的经历和自己的想法,算不上经验&#xff0c;仅供参考吧! 一面 — —技术基础&#xff0c;全面…

linux 自定义命令/别名

参考资料 Linux(Ubuntu)自定义命令的使用Linux/Ubuntu系统自定义Shell命令Ubuntu/Linux 操作系统 自定义命令 目录 一. 为路径取别名二. 修改.profile文件2.1 .profile简介2.2 需求2.3 修改.profile文件 三. 创建软链接 一. 为路径取别名 ⏹需求&#xff1a;有一个work文件夹…

API网关工具Kong或nginx ingress实现对客户端IP的白名单限制,提高对外服务的访问安全

一、背景 部署在生产环境的应用&#xff0c;供内部服务调用外&#xff0c;还需要暴露外网访问。 比如consul ui管理界面&#xff0c;我们需要给到开发和测试人员&#xff0c;观察服务的注册情况。 再比如前端页面和后端接口在一起的服务&#xff0c;后端接口供内部服务接口调用…

蓝桥杯备赛刷题——css

新鲜的蔬菜 这题需要使用grid 我不会 去学一下 一.什么是grid Grid 布局与 Flex 布局有一定的相似性&#xff0c;都可以指定容器内部多个项目的位置。但是&#xff0c;它们也存在重大区别。 Flex 布局是轴线布局&#xff0c;只能指定"项目"针对轴线的位置&#…

XTTS数据迁移方案

前置条件检查 XTTS使用限制较多&#xff0c;V3版本按照本节逐项检查 目标库操作系统不能是windows 源库&#xff1a;redhut 7.9 目标库&#xff1a;redhut 7.9 检查数据库时区&#xff08;两边都需要&#xff09; SQL> select dbtimezone from dual; 检查结果两边都一致…