Nacos服务注册解析

news2024/9/23 11:29:05

服务注册就是在微服务启动时自动注册进nacos注册中心,核心逻辑就是在启动时调用nacos-server端的http接口:/nacos/v1/ns/instance,具体参考nacos官方文档。
在这里插入图片描述
我们打开nacos源码结构查看
在这里插入图片描述
上图为Nacos2.2的源码结构.其中比较核心的就是这几个包:
nacos-api: 核心api,主要提供一些核心的接口和类,如ConfigService、ConfigFactory、Listener等;
nacos-client: Nacos客户端与服务端交互的包;
nacos-common: 通用包,提供一些通用的常量类、工具类、数据模型以及一些核心的接口等等;
nacos-config: 配置管理的服务端包,采用SpringMVC对外提供配置发布、获取、删除、监听等一系列操作接口;
nacos-consistency: 数据一致性协议相关接口,如APProtocol、CPProcotol、ConsistencyProtocol等;
nacos-core: Nacos内核包,非常核心,如nacos-consistency中一致性协议的实现、集群管理等等
nacos-naming: Nacos注册中心服务端包,采用SpringMVC对外提供关于服务注册与发现等一系列操作接口

在上面说到,客户端微服务调用了一个注册服务的接口发往客户端,那么服务端这边是怎么处理的呢

Nacos服务器使用了一个双重Map结构来保存实例数据,具体为ServiceManager类中的属性serviceMap
在这里插入图片描述
这里设计到nacos的相关参数概念
Namespace:比较好理解,主要用于环境隔离,如dev、prod等,nacos中对应数据模型:
com.alibaba.nacos.console.model.Namespace;
Group:nacos注册中心中没有Group的数据模型,我觉得这是个逻辑概念,处于同一个Namespace下的服务还可以以Group进行隔离,可以在Namespace的基础上提供更小粒度的隔离;
Service:服务信息,可以对照具体的微服务(如帐号服务、订单服务)来理解,里面封装了集群列表,而集群又封装了实例列表,nacos中对应数据模型:com.alibaba.nacos.naming.core.Service;
Cluster:集群信息,每个服务可以部署多个集群(如多机房部署,一个机房就是一个集群),nacos中对应数据模型:com.alibaba.nacos.naming.core.Cluster;
Instance:具体的服务实例信息,如果单个账号服务实例,如果是单集群部署,就会对应一个实例列表,一个实例列表都处于一个Cluster中;如果是多集群部署,那么就会存在多个Cluster,每个Cluster都会存储这样一个实例列表。nacos中对应实例的数据模型:com.alibaba.nacos.naming.core.Instance;
在这里插入图片描述
接着上面客户端发送Grpc注册服务请求后通过一系列调用会来到DistroConsistencyServiceImpl类中的put方法

    public void put(String key, Record value) throws NacosException {
         #核心注册逻辑
        this.onPut(key, value);
        if (!((UpgradeJudgement)ApplicationUtils.getBean(UpgradeJudgement.class)).isUseGrpcFeatures()) {
        #同步Instances给其他nacos集群 通过异步任务来实现,DistroProtocol类是Distro协议的其中一个核心实现
            this.distroProtocol.sync(new DistroKey(key, "com.alibaba.nacos.naming.iplist."), DataOperation.CHANGE, DistroConfig.getInstance().getSyncDelayMillis());
        }
    }

其onPut方法中会调用notifier.addTask往阻塞队列新增Instances

    public void onPut(String key, Record value) {
        if (KeyBuilder.matchEphemeralInstanceListKey(key)) {
            Datum<Instances> datum = new Datum();
            datum.value = (Instances)value;
            datum.key = key;
            datum.timestamp.incrementAndGet();
            this.dataStore.put(key, datum);
        }

        if (this.listeners.containsKey(key)) {
        	# 调用本类的线程池新增
            this.notifier.addTask(key, DataOperation.CHANGE);
        }
    }

接下来我们进入addTask具体的实现,这也是nacos服务注册的核心逻辑,通过线程put进阻塞队列的设计。

    public class Notifier implements Runnable {
        private ConcurrentHashMap<String, String> services = new ConcurrentHashMap(10240);
        #阻塞队列
        private BlockingQueue<Pair<String, DataOperation>> tasks = new ArrayBlockingQueue(1048576);

        public Notifier() {
        }

        public void addTask(String datumKey, DataOperation action) {
            if (!this.services.containsKey(datumKey) || action != DataOperation.CHANGE) {
                if (action == DataOperation.CHANGE) {
                    this.services.put(datumKey, "");
                }
				#BlockingQueue接口方法 往队列尾部添加元素,不会阻塞
				#至此服务注册流程已经结束
                this.tasks.offer(Pair.with(datumKey, action));
            }
        }

既然是队列存储,那一定是会有地方从此队列取出来进行消费出来,我们可以找到在DistroConsistencyServiceImpl类的init()方法中发现,且该方法还标注了一个@PostConstruct注解
我们继续查看Notifier线程的run方法

        public void run() {
            Loggers.DISTRO.info("distro notifier started");
			#死循环从阻塞队列取数据处理。
            while(true) {
                while(true) {
                    try {
                    	#BlockingQueue接口take方法 获取队列头部第1个元素,会一直阻塞直到取得元素或当前线程中断。
                        Pair<String, DataOperation> pair = (Pair)this.tasks.take();
                        this.handle(pair);
                    } catch (Throwable var2) {
                        Loggers.DISTRO.error("[NACOS-DISTRO] Error while handling notifying task", var2);
                    }
                }
            }
        }

相当于这个Notifier线程只干了一件事,在线程启动后就死循环扫描该队列,并获取数据处理,我们再次点击进去handler方法查看数据如何处理的。

        private void handle(Pair<String, DataOperation> pair) {
            try {
                String datumKey = (String)pair.getValue0();
                #获取此处数据的操作方式
                DataOperation action = (DataOperation)pair.getValue1();
                this.services.remove(datumKey);
                int count = 0;
                if (!DistroConsistencyServiceImpl.this.listeners.containsKey(datumKey)) {
                    return;
                }

                Iterator var5 = ((ConcurrentLinkedQueue)DistroConsistencyServiceImpl.this.listeners.get(datumKey)).iterator();

                while(var5.hasNext()) {
                	#获取监听器列表
                    RecordListener listener = (RecordListener)var5.next();
                    ++count;

                    try {
                        if (action == DataOperation.CHANGE) {
                        	#向各监听器调用更新注册表
                            listener.onChange(datumKey, DistroConsistencyServiceImpl.this.dataStore.get(datumKey).value);
                        } else if (action == DataOperation.DELETE) {
                            listener.onDelete(datumKey);
                        }
                    } catch (Throwable var8) {
                        Loggers.DISTRO.error("[NACOS-DISTRO] error while notifying listener of key: {}", datumKey, var8);
                    }
                }

debug可以发现最终到了Service类的onChange方法
这里主要是更改Instance相对于的权重属性

    public void onChange(String key, Instances value) throws Exception {
        Loggers.SRV_LOG.info("[NACOS-RAFT] datum is changed, key: {}, value: {}", key, value);
        Iterator var3 = value.getInstanceList().iterator();

        while(var3.hasNext()) {
            Instance instance = (Instance)var3.next();
            if (instance == null) {
                throw new RuntimeException("got null instance " + key);
            }

            if (instance.getWeight() > 10000.0) {
                instance.setWeight(10000.0);
            }

            if (instance.getWeight() < 0.01 && instance.getWeight() > 0.0) {
                instance.setWeight(0.01);
            }
        }
		#核心更新逻辑
        this.updateIPs(value.getInstanceList(), KeyBuilder.matchEphemeralInstanceListKey(key));
        this.recalculateChecksum();
    }

我们直接进入到updateIPs方法中,这里使用了copyonwrite的思想更新注册表,关键拷贝的不是整个注册表而是注册表中的Cluster实例列表,因此如果注册表中数据很多,最终拷贝的也只是很小一部分不会产生什么影响

Nacos如何解决多节点读写并发冲突

copyonwrite:适合读多写少的情况,最大程度的提高读的效率,牺牲空间,换取性能的思想

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

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

相关文章

墨者学院 PHP代码分析溯源(第4题) 详解

今天继续给大家介绍CTF通关writeup&#xff0c;本文主要内容是墨者学院 PHP代码分析溯源(第4题)。 免责声明&#xff1a; 本文所介绍的内容仅做学习交流使用&#xff0c;严禁利用文中技术进行非法行为&#xff0c;否则造成一切严重后果自负&#xff01; 再次强调&#xff1a;严…

尝试使用CubeMX做stm32开发之十四:FatFs的基础知识

一、文件系统概念 文件系统&#xff1a;在存储介质中建立一种组织架构&#xff0c;以更好地储存和管理数据 文件系统一般包含&#xff1a;操作系统引导区、目录、文件等 文件系统中数据以文件的形式存储 文件系统中数据的存取需要遵循特定的格式 与直接操作存储介质相比&a…

[附源码]计算机毕业设计基于SpringBt的演唱会购票系统论文2022Springboot程序

项目运行 环境配置&#xff1a; Jdk1.8 Tomcat7.0 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目技术&#xff1a; SSM mybatis Maven Vue 等等组成&#xff0c;B/S模式 M…

C++ MySQL Error 1366 incorrect string value引发的认识

C MySQL Error 1366 incorrect string value引发的认识 前言 在使用MySQL C API编写程序时&#xff0c;由于用到了中文&#xff0c;导致出现了MySQL error 1366 incorrect string value 问题&#xff0c;但令我同样不解的是我用同样的语句在cmd下可以正常执行。&#xff08;M…

【算法】动态规划 ⑤ ( LeetCode 63.不同路径 II | 问题分析 | 动态规划算法设计 | 代码示例 )

文章目录一、问题分析二、动态规划算法设计1、动态规划状态 State2、动态规划初始化 Initialize3、动态规划方程 Function4、动态规划答案 Answer三、代码示例LeetCode 63. 不同路径 II : https://leetcode.cn/problems/unique-paths-ii/ 一个机器人位于一个 m x n 网格的左上…

java初步学习 String(基于小李的课进行自学,初学者)12

初步学习 String 基本概念 String类型即为字符串类型&#xff0c;即“103”&#xff0c;“abc”,"小陈"等字符类型&#xff0c;在工作中可用于加密&#xff0c;替换&#xff0c;截取&#xff0c;查找等工作 不过这种名词显然不能联想到工作的实际样子 例&#xff1a;…

[附源码]Python计算机毕业设计SSM基于框架的旅游订票系统(程序+LW)

项目运行 环境配置&#xff1a; Jdk1.8 Tomcat7.0 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目技术&#xff1a; SSM mybatis Maven Vue 等等组成&#xff0c;B/S模式 M…

IIS 之 添加MIME扩展类型及HTTP错误403

IIS 之 添加MIME扩展类型及HTTP错误403 我是艾西今天跟大家分享下IIS添加MIME及HTTP报错403 经常用IIS作为下载服务器的时候有时传上去的文件比如 example.mp4 文件名上传后&#xff0c;但是用http打开的时候确显示为 404 文件不存在。其实是IIS对文件的一种保护&#xff0c;…

基于java+springboot+mybatis+vue+mysql的校园志愿者管理系统

项目介绍 本校园志愿者管理系统以springboot作为框架&#xff0c;前端vue技术&#xff0c;b/s模式以及mysql作为后台运行的数据库&#xff0c;同时使用Tomcat用为系统的服务器。本系统主要包括首页、个人中心、志愿者管理、活动类型管理、活动信息管理、活动报名管理、活动通知…

游戏开发49课 性能优化7

4. 渲染优化 渲染优化的目的是减少Draw Calls&#xff0c;减少渲染状态切换开销&#xff0c;降低显存占用&#xff0c;降低带宽和GPU负担。在讲解渲染优化之前&#xff0c;先了解渲染性能消耗点。 Draw Call数量 Draw Call有些引擎也称为SetPass Call。一个Draw Call就是游戏调…

叫ChatGPT用html+css+js写一个圣诞节代码,看看什么样子?

最近ChatGPT这么火&#xff0c;那就让他给我写点代码吧。 如何注册一个账号&#xff0c;参考&#xff1a;注册ChatGPT详细指南 注册不了的小伙伴们&#xff0c;咱们评论区见&#xff0c;问一个最想问的问题&#xff0c;看到就给你回复&#xff01; 我已经注册好了&#xff0c;…

代码随想录算法训练营第五十九天|503.下一个更大元素II、42. 接雨水

LeetCode 503.下一个更大元素II 链接&#xff1a;503.下一个更大元素II 思路&#xff1a; 本题其实和739. 每日温度更像一点&#xff0c;因为本题只有一个数组&#xff0c;而在下一个更大元素I中有两个&#xff0c;因此必须要一个哈希表来在另一个数组中查找相对应的数字。除…

yolov5检测结果不显示

安装完yolov5后&#xff0c;运行各种正常&#xff0c;后台也能显示识别信息&#xff0c;像这样。 而且在runs/detect/exp4文件夹中也会有正确的标识 但是我也想能在前台实时显示&#xff0c;像这样。 折腾了一个晚上也没有搞的定&#xff0c;甚至一度以为&#xff0c;是因为CPU…

SSM甜品店系统计算机专业毕业论文java毕业设计开题报告

&#x1f496;&#x1f496;更多项目资源&#xff0c;最下方联系我们✨✨✨✨✨✨ 目录 Java项目介绍 资料获取 Java项目介绍 计算机毕业设计java毕设之SSM甜品店系统-IT实战营_哔哩哔哩_bilibili项目资料网址: http://itzygogogo.com软件下载地址:http://itzygogogo.com/i…

【MindStudio训练营第一期】【昇腾AI训练营新手班学习笔记】可视化流程编排

准备 配置环境理解Python代码部分mxVision用户指南一份 步骤 案例的流程图&#xff1a; 图像输入和图像预处理 图像输入&#xff08;appsrc插件&#xff09; 通过python open和read到的图片数据&#xff0c;用SendData方法传入stream中&#xff0c;appsrc将数据发送给下游元…

《操作系统》期末考试卷3参考答案

《操作系统》期末考试卷&#xff08;第3套&#xff09; 参考答案与评分标准 一、单项选择题&#xff08;共10题&#xff0c;每题2分&#xff0c;共20分&#xff09; 题号 1 2 3 4 5 6 7 8 9 10 答案 D D C A C B A A B A 二、填空题&#xff08;共8题&#xff0c;每空1分&…

Java学习之toString方法

目录 toString方法的功能 Object类的toString方法 源代码 子类没有重写的案例 子类重写toString 使用AltInsert自动重写 输出结果 最后一条 例子 toString方法的功能 返回该对象的字符串表示。默认返回&#xff1a;全类名&#xff08;包名类名&#xff09;哈希值的十六进…

【云服务器 ECS 实战】ECS 快照镜像的原理及用法详解

一、ECS 快照1. 阿里云 ECS 快照概述• ECS 增量快照机制2. 快照服务的开通与使用• 开通快照与 OSS 资源包的购买• 手动快照• 自动快照二、ECS 镜像1. 阿里云 ECS 镜像概述2. 自定义镜像的创建与使用• 导入镜像• 自定义镜像一、ECS 快照 1. 阿里云 ECS 快照概述 阿里云快…

[ Linux ] 进程信号递达,阻塞,捕捉

目录 1.core dump字段 1.1 Core dump是什么&#xff1f; 1.2 用代码看看Core Dump 1.3 core dump的作用 core dump一般会被关掉 2.阻塞信号 2.1 信号其他相关常见概念 2.2在内核中的表示 3.信号产生中 3.1 sigset_t 3.2信号集操作函数 3.2.1 sigprocmask 3.2.2 si…

Carla学习2:carla安装与使用

文章目录0. 建议1. carla学习相关链接1.1 官方资料1.1 学习教程2. Carla安装2.1 服务器端2.1.1 下载预编译版本&#xff08;也可以使用下载源码并编译&#xff09;2.1.2 启动服务器端及服务器端显示导航2.3 客户端2.3.1 创建python环境2.3.2 安装carla 的pythonAPI所需要的依赖…