16 Nacos服务端服务注册源码分析
Nacos服务端服务注册源码分析
服务端调用接口
我们已经知道客户端在注册服务的时候实际上是调用的NamingService.registerInstance这个方法来完成实例的注册,而且在最后我们也告诉了大家实际上从本质上讲服务注册就是调用的对应接口nacos/v1/ns/instance,那咱们现在就在服务端先找到这个接口,然后来看具体服务端的操作。
这是从Nacos官网上我们看到的Nacos架构图,其实在这里我们已经就能分析出我们要找的接口应该在NamingService这个服务中,从源码角度来看,其实通过一下这个项目结构图中我们也能清楚的看见naming这个子模块,这个naming实际上就是实现服务的注册的。
那我们接着来向下看这个项目中的controller,因为我们知道所有的接口其实都在controller中,从这些Controller中我们就会明显的看到一个InstanceController,所以很明显注册实例一定和它有关
所以我们打开InstanceController来深入研究一下,这个时候会发现@RequestMapping注解中的值就是我们访问的注册接口
接下来我们再来寻找RESTful API接口POST请求类型的方法register,在这个方法中实际上就是接受用户请求,把收到的信息进行解析,还原成Instance,然后调用registerInstance方法来完成注册,这个方法才是服务端注册的核心
@CanDistro
@PostMapping
@Secured(parser = NamingResourceParser.class, action = ActionTypes.WRITE)
public String register(HttpServletRequest request) throws Exception {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);final Instance instance = HttpRequestInstanceBuilder.newBuilder().setDefaultInstanceEphemeral(switchDomain.isDefaultInstanceEphemeral()).setRequest(request).build();//注册服务实例getInstanceOperator().registerInstance(namespaceId, serviceName, instance);return "ok";
}
我们注意一下的这个方法
getInstanceOperator().registerInstance(namespaceId, serviceName, instance);
其中的getInstanceOperator(),就是判断是否采用Grpc协议,很明显这个位置走的是instanceServiceV2
private InstanceOperator getInstanceOperator() {return upgradeJudgement.isUseGrpcFeatures() ? instanceServiceV2 : instanceServiceV1;
服务注册
instanceServiceV2.registerInstance
实际上instanceServiceV2就是InstanceOperatorClientImpl,所以我们来看这里面的registerInstance方法.
@Override
public void registerInstance(String namespaceId, String serviceName, Instance instance) {//判断是否为瞬时对象(临时客户端)boolean ephemeral = instance.isEphemeral();//获取客户端IDString clientId = IpPortBasedClient.getClientId(instance.toInetAddr(), ephemeral);//通过客户端ID创建客户端连接createIpPortClientIfAbsent(clientId);//获取服务Service service = getService(namespaceId, serviceName, ephemeral);//具体注册服务clientOperationService.registerInstance(service, instance, clientId);
}
在这里我们要分析一些细节,其实Nacos2.0以后新增Client模型。一个客户端gRPC长连接对应一个Client,每个Client有自己唯一的id(clientId)。Client负责管理一个客户端的服务实例注册Publish和服务订阅Subscribe。我们可以看一下这个模型其实就是一个接口(为了大家看着方便,注释改成了中文)
public interface Client {// 客户端id/gRPC的connectionIdString getClientId();// 是否临时客户端boolean isEphemeral();// 客户端更新时间void setLastUpdatedTime();long getLastUpdatedTime();// 服务实例注册/注销/查询boolean addServiceInstance(Service service, InstancePublishInfo instancePublishInfo);InstancePublishInfo removeServiceInstance(Service service);InstancePublishInfo getInstancePublishInfo(Service service);Collection<Service> getAllPublishedService();// 服务订阅/取消订阅/查询订阅boolean addServiceSubscriber(Service service, Subscriber subscriber);boolean removeServiceSubscriber(Service service);Subscriber getSubscriber(Service service);Collection<Service> getAllSubscribeService();// 生成同步给其他节点的client数据ClientSyncData generateSyncData();// 是否过期boolean isExpire(long currentTime);// 释放资源void release();
}
EphemeralClientOperationServiceImpl.registerInstance
EphemeralClientOperationServiceImpl实际负责处理服务注册,那我们来看具体方法
@Override
public void registerInstance(Service service, Instance instance, String clientId) {//确保Service单例存在Service singleton = ServiceManager.getInstance().getSingleton(service);//根据客户端id,找到客户端Client client = clientManager.getClient(clientId);if (!clientIsLegal(client, clientId)) {return;}//客户端Instance模型,转换为服务端Instance模型InstancePublishInfo instanceInfo = getPublishInfo(instance);//将Instance储存到Client里client.addServiceInstance(singleton, instanceInfo);client.setLastUpdatedTime();//建立Service与ClientId的关系NotifyCenter.publishEvent(new ClientOperationEvent.ClientRegisterServiceEvent(singleton, clientId));NotifyCenter.publishEvent(new MetadataEvent.InstanceMetadataEvent(singleton, instanceInfo.getMetadataId(), false));
}
ServiceManager
Service的容器是ServiceManager,但是在com.alibaba.nacos.naming.core.v2包下,容器中Service都是单例。
public class ServiceManager {private static final ServiceManager INSTANCE = new ServiceManager();//单例Service,可以查看Service的equals和hasCode方法private final ConcurrentHashMap<Service, Service> singletonRepository;//namespace下的所有serviceprivate final ConcurrentHashMap<String, Set<Service>> namespaceSingletonMaps;.....
所以从这个位置可以看出,当调用这个注册方法的时候ServiceManager负责管理Service单例
//通过Map储存单例的Service
public Service getSingleton(Service service) {singletonRepository.putIfAbsent(service, service);Service result = singletonRepository.get(service);namespaceSingletonMaps.computeIfAbsent(result.getNamespace(), (namespace) -> new ConcurrentHashSet<>());namespaceSingletonMaps.get(result.getNamespace()).add(result);return result;
}
clientManager
这是一个接口这里我们要看它对应的一个实现类ConnectionBasedClientManager,这个实现类负责管理长连接clientId与Client模型的映射关系
// 根据clientId查询Client
public Client getClient(String clientId) {return clients.get(clientId);
}
Client实例AbstractClient
负责存储当前客户端的服务注册表,即Service与Instance的关系。注意对于单个客户端来说,同一个服务只能注册一个实例。
@Override
public boolean addServiceInstance(Service service, InstancePublishInfo instancePublishInfo) {if (null == publishers.put(service, instancePublishInfo)) {MetricsMonitor.incrementInstanceCount();}NotifyCenter.publishEvent(new ClientEvent.ClientChangedEvent(this));Loggers.SRV_LOG.info("Client change for service {}, {}", service, getClientId());return true;
}
ClientOperationEvent.ClientRegisterServiceEvent
这里的目的是为了过滤目标服务得到最终Instance列表建立Service与Client的关系,建立Service与Client的关系就是为了加速查询。
发布ClientRegisterServiceEvent事件,ClientServiceIndexesManager监听,ClientServiceIndexesManager维护了两个索引:
- Service与发布clientId
- Service与订阅clientId
private final ConcurrentMap<Service, Set<String>> publisherIndexes = new ConcurrentHashMap<>();private final ConcurrentMap<Service, Set<String>> subscriberIndexes = new ConcurrentHashMap<>();private void handleClientOperation(ClientOperationEvent event) {Service service = event.getService();String clientId = event.getClientId();if (event instanceof ClientOperationEvent.ClientRegisterServiceEvent) {addPublisherIndexes(service, clientId);} else if (event instanceof ClientOperationEvent.ClientDeregisterServiceEvent) {removePublisherIndexes(service, clientId);} else if (event instanceof ClientOperationEvent.ClientSubscribeServiceEvent) {addSubscriberIndexes(service, clientId);} else if (event instanceof ClientOperationEvent.ClientUnsubscribeServiceEvent) {removeSubscriberIndexes(service, clientId);}
}//建立Service与发布Client的关系
private void addPublisherIndexes(Service service, String clientId) {publisherIndexes.computeIfAbsent(service, (key) -> new ConcurrentHashSet<>());publisherIndexes.get(service).add(clientId);NotifyCenter.publishEvent(new ServiceEvent.ServiceChangedEvent(service, true));
}
这个索引关系建立以后,还会触发ServiceChangedEvent,代表服务注册表变更。对于注册表变更紧接着还要做两个事情:1.通知订阅客户端 2.Nacos集群数据同步。
相关文章:

16 Nacos服务端服务注册源码分析
Nacos服务端服务注册源码分析 服务端调用接口 我们已经知道客户端在注册服务的时候实际上是调用的NamingService.registerInstance这个方法来完成实例的注册,而且在最后我们也告诉了大家实际上从本质上讲服务注册就是调用的对应接口nacos/v1/ns/instanceÿ…...
Spring Boot2中如何优雅地个性化定制Jackson
概述 本文的编写初衷,是想了解一下Spring Boot2中,具体是怎么序列化和反序列化JSR 310日期时间体系的,Spring MVC应用场景有如下两个: 使用RequestBody来获取JSON参数并封装成实体对象;使用ResponseBody来把返回给前…...
2023年全国最新食品安全管理员精选真题及答案11
百分百题库提供食品安全管理员考试试题、食品安全员考试预测题、食品安全管理员考试真题、食品安全员证考试题库等,提供在线做题刷题,在线模拟考试,助你考试轻松过关。 101.婴幼儿配方乳粉的产品配方应当经()部门注册。…...

【脚本】用于得到某个文件/文件夹所有文件的存储大小(MB单位)
知识点 来自在线转换换算网页:在线文件大小(bit,bytes,KB,MB,GB,TB)转换换算 电脑中存储常用的单位: 1Byte(Byte 字节) 8Bit 1KB (Kilobyte 千字节) 1024Byte 1MB (Megabyte,兆字节,简称“兆”) 1024KB 1GB (Gigabyte&am…...

19- CNN进行Fashion-MNIST分类 (tensorflow系列) (项目十九)
项目要点 Fashion-MNIST总共有十个类别的图像。代码运行位置 CPU: cputf.config.set_visible_devices(tf.config.list_physical_devices("CPU"))fashion_mnist keras.datasets.fashion_mnist # fashion_mnist 数据导入训练数据和测试数据拆分: x_valid, x_train…...

【正点原子FPGA连载】第二十二章IP封装与接口定义实验 摘自【正点原子】DFZU2EG_4EV MPSoC之嵌入式Vitis开发指南
1)实验平台:正点原子MPSoC开发板 2)平台购买地址:https://detail.tmall.com/item.htm?id692450874670 3)全套实验源码手册视频下载地址: http://www.openedv.com/thread-340252-1-1.html 第二十二章IP封装…...

【ElasticSearch8.X】学习笔记(二)
【ElasticSearch8.X】学习笔记四、基础操作4.1、索引操作4.1.1、创建索引4.1.2、查询指定索引4.1.3、查询所有索引4.1.4、 删除索引4.2、文档操作4.2.1、创建文档4.2.2、查询文档4.2.3、修改文档4.2.4、删除文档4.2.5、查询所有文档4.3、数据搜索4.3.1、匹配查询文档4.3.2、匹配…...

Ubuntu22.04安装、配置、美化、软件安装、配置开发环境
Ubuntu22.04安装、配置、美化、软件安装、配置开发环境 一、Ubuntu、Windows11(10)双系统安装 因为ubuntu的安装网上的教程特别多了,所以这里不做赘述,推荐使用小破站这个up主的教程:Windows 和 Ubuntu 双系统从安装到…...

企业电子招投标采购系统之系统的首页设计
功能模块: 待办消息,招标公告,中标公告,信息发布 描述: 全过程数字化采购管理,打造从供应商管理到采购招投标、采购合同、采购执行的全过程数字化管理。通供应商门户具备内外协同的能力,为…...

Python爬虫-阿里翻译_csrf
前言 本文是该专栏的第37篇,后面会持续分享python爬虫干货知识,记得关注。 笔者在前面有介绍过百度翻译的案例,感兴趣的同学,可往前翻阅查看(JS逆向-百度翻译sign)。而本文,笔者要介绍的是阿里翻译,相对于百度翻译的参数被逆向需要花点时间,阿里相对于易上手。 下面…...

C语言实现三子棋【详解+全部源码】
大家好,我是你们熟悉的恒川 今天我们用C语言来实现三子棋 实现的过程很难,但我们一定要不放弃 三子棋1. 配置运行环境2. 三子棋游戏的初步实现2.1 建立三子棋分布模块2.2 创建一个名为board的二维数组并进行初始化2.3 搭建棋盘3. 接下来该讨论的事情3.1 …...

双指针法将时间复杂度从 O(n^2) 优化到 O(n)
[1] 什么是双指针法 双指针法(Two Pointers)是一种常见的算法技巧,常用于数组和链表等数据结构中。 双指针法的基本思想是维护两个指针,分别指向不同的位置,通过它们的移动来解决问题。在某些情况下,使用双…...
【SpringBoot系列】 Spring中自定义Session管理,Spring Session源码解析
系列文章:Spring Boot学习大纲,可以留言自己想了解的技术点 目录 系列文章:Spring Boot学习大纲,可以留言自己想了解的技术...

【上位机入门常见问题】SQLServer2019 安装指导
SQLServer2019 安装指导 这里要说一下SQLServer的版本问题,首先说纵向的高低版本,如果大家跟我学习,我教给大家的是T-SQL编程的方法,而不是直接操作菜单的方法,所以,我们学习中只要使用SQLServer2012或以上…...

RabbitMQ第一讲
目录 一、RabbitMQ-01 1.1 MQ概述 1.2 MQ的优势和劣势 1.2.1 优势 1.2.2 劣势 1.2.3 MQ应用场景 1.2.4 常用的MQ产品 1.3 RabbitMQ的基本介绍 1.3.1 AMQP介绍 1.3.2 RabbitMQ基础架构 1.3.3 RabbitMQ的6种工作模式 编辑 1.4 AMQP和JMS 1.4.1 AMQP 1.4.2 JMS …...

华为机试题:HJ100 等差数列(python)
文章目录(1)题目描述(2)Python3实现(3)知识点详解1、input():获取控制台(任意形式)的输入。输出均为字符串类型。1.1、input() 与 list(input()) 的区别、及其相互转换方…...

数据推荐 | 人体行为识别数据集
人体行为识别任务旨在通过对人体姿态进行分析,识别出人体的具体动作,为人体行为预测、突发事件处理、智能健身、智能看护等领域提供技术支持。 图片 图片 人体行为识别数据标注方式 人体行为数据通用的标注方式包括人体关键点标注和动作标签标注&#…...
667真题分析 | 2023年667真题简要分析和答题思路参考
2023年667真题简要分析和答题思路参考 文章目录 2023年667真题简要分析和答题思路参考前言1. 名词解释2. 简答题3. 分析题3.1 答题框架(套路)3.2 答题框架实战3.2.1 图书情报档案事业如何在文化自信、文化强国中发挥自己的地位和作用3.2.2 高校图书馆如何发挥空间资源的功能和…...
配置 Docker 使用 GPU
准备工作 首先你需要准备一台拥有GPU的实例,在这里我将使用阿里云的竞价实例来做演示,因为它对于短期使用GPU更加划算。 注意,本篇文章将教你手动进行GPU驱动的配置,所以在购买时选择系统的时候不要选择自动安装GPU驱动。 具体关…...

「并发编程实战」常见的限流方案
「并发编程实战」常见的限流方案 文章目录「并发编程实战」常见的限流方案一、概述二、计数器限流方案三、时间窗口限流方案四、令牌桶限流方案五、漏桶限流方案六、高并发限流算法小结文章参考: 追忆四年前:一段关于我被外企CTO用登录注册吊打的不堪往事…...

日语AI面试高效通关秘籍:专业解读与青柚面试智能助攻
在如今就业市场竞争日益激烈的背景下,越来越多的求职者将目光投向了日本及中日双语岗位。但是,一场日语面试往往让许多人感到步履维艰。你是否也曾因为面试官抛出的“刁钻问题”而心生畏惧?面对生疏的日语交流环境,即便提前恶补了…...
【杂谈】-递归进化:人工智能的自我改进与监管挑战
递归进化:人工智能的自我改进与监管挑战 文章目录 递归进化:人工智能的自我改进与监管挑战1、自我改进型人工智能的崛起2、人工智能如何挑战人类监管?3、确保人工智能受控的策略4、人类在人工智能发展中的角色5、平衡自主性与控制力6、总结与…...
DeepSeek 赋能智慧能源:微电网优化调度的智能革新路径
目录 一、智慧能源微电网优化调度概述1.1 智慧能源微电网概念1.2 优化调度的重要性1.3 目前面临的挑战 二、DeepSeek 技术探秘2.1 DeepSeek 技术原理2.2 DeepSeek 独特优势2.3 DeepSeek 在 AI 领域地位 三、DeepSeek 在微电网优化调度中的应用剖析3.1 数据处理与分析3.2 预测与…...
React Native 开发环境搭建(全平台详解)
React Native 开发环境搭建(全平台详解) 在开始使用 React Native 开发移动应用之前,正确设置开发环境是至关重要的一步。本文将为你提供一份全面的指南,涵盖 macOS 和 Windows 平台的配置步骤,如何在 Android 和 iOS…...
sqlserver 根据指定字符 解析拼接字符串
DECLARE LotNo NVARCHAR(50)A,B,C DECLARE xml XML ( SELECT <x> REPLACE(LotNo, ,, </x><x>) </x> ) DECLARE ErrorCode NVARCHAR(50) -- 提取 XML 中的值 SELECT value x.value(., VARCHAR(MAX))…...

MFC 抛体运动模拟:常见问题解决与界面美化
在 MFC 中开发抛体运动模拟程序时,我们常遇到 轨迹残留、无效刷新、视觉单调、物理逻辑瑕疵 等问题。本文将针对这些痛点,详细解析原因并提供解决方案,同时兼顾界面美化,让模拟效果更专业、更高效。 问题一:历史轨迹与小球残影残留 现象 小球运动后,历史位置的 “残影”…...
【学习笔记】erase 删除顺序迭代器后迭代器失效的解决方案
目录 使用 erase 返回值继续迭代使用索引进行遍历 我们知道类似 vector 的顺序迭代器被删除后,迭代器会失效,因为顺序迭代器在内存中是连续存储的,元素删除后,后续元素会前移。 但一些场景中,我们又需要在执行删除操作…...
Spring Security 认证流程——补充
一、认证流程概述 Spring Security 的认证流程基于 过滤器链(Filter Chain),核心组件包括 UsernamePasswordAuthenticationFilter、AuthenticationManager、UserDetailsService 等。整个流程可分为以下步骤: 用户提交登录请求拦…...

CTF show 数学不及格
拿到题目先查一下壳,看一下信息 发现是一个ELF文件,64位的 用IDA Pro 64 打开这个文件 然后点击F5进行伪代码转换 可以看到有五个if判断,第一个argc ! 5这个判断并没有起太大作用,主要是下面四个if判断 根据题目…...

工厂方法模式和抽象工厂方法模式的battle
1.案例直接上手 在这个案例里面,我们会实现这个普通的工厂方法,并且对比这个普通工厂方法和我们直接创建对象的差别在哪里,为什么需要一个工厂: 下面的这个是我们的这个案例里面涉及到的接口和对应的实现类: 两个发…...