16 图总结 Nacos 一个服务注册请求会经历什么? 📅 发布时间:2026/7/12 5:43:27 👁️ 浏览次数: 前言上篇我们讲解了如何使用 Nacos 作为注册中心和配置中心。这次我们来聊下 Nacos 的注册服务的底层原理。Nacos 作为注册中心用来接收客户端服务实例发起的注册请求并将注册信息存放到注册中心进行管理。那么一条注册请求到底会经历哪些步骤呢知识点预告先上一张整体的流程图集群环境如果是 Nacos 集群环境那么拓扑结构是什么样的。组装请求客户端组装注册请求下一步对 Nacos 服务发起远程调用。随机节点客户端随机选择集群中的一个 Nacos 节点发起注册实现负载均衡。路由转发Nacos 节点收到注册请求后看下是不是属于自己的不是的话就进行路由转发。处理请求转发给指定的节点后该节点就会将注册请求中的实例信息解析出来存到自定义的内存结构中。最终一致性通过 Nacos 自研的 Distro 协议执行延迟异步任务将注册信息同步给集群中的其他节点保证了数据的最终一致性。异步重试如果注册失败客户端将会切换 Nacos 节点再次发起注册请求保证高可用性。这些知识点里面还有很多细节我会通过画图 源码剖析的方式给大家解答。如果遇到源码看不太懂的地方可以多看下我画的图然后翻下源码对照着一起看。小 Tip本文使用的 Nacos 版本2.0.4。一、源头发起注册1.1 阅读源码的小技巧上篇我们讲到加上一个注解EnableDiscoveryClient就可以使服务自动注册到 Nacos。那么这个发起注册的地方到底在哪呢注册信息又是长什么样的呢告诉大家一个看源码的小技巧拿到源码后不是直接各个文件都看一篇而是先看源码中带的 example 文件夹。如下图所示找到 example 的 App 类里面就有发起注册的实例代码。如下图所示当然我们也可以通过官网给的 curl 命令发起 HTTP 请求curl -X POST http://127.0.0.1:8848/nacos/v1/ns/instance?serviceNamenacos.naming.serviceNameip20.18.7.11port8080留个问题我们都是加一个 Nacos 注解EnableDiscoveryClient就会自动把服务实例注册到 Nacos这个是怎么做到的1.2 发起注册的流程图先来看一下代码的流程图跟着这个流程图我们 debug 来看下。1.3 组装注册的实例信息入口的核心代码如下图所示它会组装注册的实例信息放到一个 instance 变量里面通过代码调试我们可以看到里面的实例信息长这样1.4 组装注册请求 request发起注册的核心方法是 doRegisterService()组装的 request 如下图所示里面有之前组装的实例信息 instance还有指定的 namespaceNacos 的命名空间、serviceName服务名groupNameNacos 的分组。发起注册的源码1.5 发起远程调用requestToServer() 方法里面会调用 RpcClient 的 request() 方法response this.currentConnection.request(request, timeoutMills);就是向 Nacos 发起远程调用如果是 Nacos 集群则是向集群中的某个 Nacos 节点发起远程调用。接下来我们看下客户端是如何选择一个 Nacos 节点进行注册的。二、集群环境分布式的前提如果是 Nacos 集群环境客户端会随机选择一个 Nacos 节点发起注册。2.1 搭建好一套Nacos 集群环境为了讲解客户端是如何注册到 Nacos 集群环境的底层原理我在本地搭建了一个 Nacos 集群环境有 3 个 Nacos 服务它们的 IP 相同端口号不同。192.168.10.197:8848 192.168.10.197:8858 192.168.10.197:8868集群环境然后服务 A 和服务 B 都是配置了 Nacos 集群的 IP 和 端口号的配置如下所示spring.cloud.nacos.discovery.server-addr 192.168.10.197:8848,192.168.10.197:8858,192.168.10.197:8868整体的结构如下图所示服务 A 和 服务 B 都往 Nacos 集群进行注册。服务 A 和 B 注册到集群但是里面有一个问题服务 A 注册时是向所有 Nacos 节点发起注册呢还是只向其中一个节点发起注册如果只向一个节点注册要向哪个节点注册呢答案在 Client 发起注册之前会有一个后台线程随机拿到 Nacos 集群服务列表中的一个地址。Nacos 为什么会这样设计这其实就是一个负载均衡的思想在里面每个节点都均匀的分摊请求。保证高可用当某个节点宕机后重新拿到其他的 Nacos 节点来建立连接。接下来我们看下服务 A 是怎么随机拿到一个 Nacos 节点的。三、随机节点平等的世界我们来看下客户端是如何随机选择一个节点的流程图如下那么如何找到这些代码逻辑呢思路是怎么样的我们之前讲过RpcClient 会发起 request 请求用的是和 Nacos 建立currentConnection连接来发起调用代码如下// 发起调用 response this.currentConnection.request(request, timeoutMills);这个currentConnection是客户端和 Nacos 集群中的某个节点建立的连接我们找下它在哪里赋值的。代码如下// 拿到 Nacos 节点信息 serverInfo recommendServer.get() null ? nextRpcServer() : recommendServer.get(); // 连接 Nacos 节点 connectToServer connectToServer(serverInfo); // 赋值 currentConnection this.currentConnection connectToServer;而连接的信息是通过参数 serverInfo 传进去的所以我们再看下 serverInfo 在哪里赋值的。这个 nextRpcServer() 方法里面会拿到一个随机的 Nacos 地址// 一个 int 随机数范围 [0 ~ Nacos 个数) currentIndex.set(new Random().nextInt(serverList.size())); // index 自增 1 int index currentIndex.incrementAndGet() % getServerList().size(); // 返回 Nacos 地址 return getServerList().get(index);小结客户端生成一个随机数然后通过这个随机数从 Nacos 服务列表中拿到一个 Nacos 服务地址返回给客户端然后客户端通过这个地址和 Nacos 服务建立连接。Nacos 服务列表中的节点都是平等的随机拿到的任何一个节点都是可以用来发起调用的。四、路由转发不是我的菜4.1 发起和转发请求的流程为了演示发起注册的流程我在这里模拟了一个注册请求。用的是 curl 命令对 Nacos 节点127.0.0.1:8848发起注册请求curl -X POST http://127.0.0.1:8848/nacos/v1/ns/instance?serviceNamenacos.naming.serviceNameip20.18.7.11port8080请求 URL/nacos/v1/ns/instance请求参数serviceNamenacos.naming.serviceNameip20.18.7.11port8080之前我们讲到Nacos 的有多个节点可以分别处理请求当节点发现这个请求不是属于自己的就会进行转发。如下图所示服务 A 随机选择一个 Nacos 节点图中为 Nacos1发起注册请求请求参数中包含了实例信息Nacos 1 根据实例信息 hash 取模拿到正确的节点如果不属于自己则将请求转发给其他节点图中为 Nacos2那么路由转发的细节是怎么样的这个就涉及到 Distro 协议了我们接着往下看。4.1 路由转发的逻辑其实 Nacos 节点的路由转发逻辑比较简单先来看下流程图步骤如下① Nacos 节点从客户端发起的 request 中拿到客户端的实例信息生成 distroTag如 IP port 或 service name。② Nacos 根据 distroTag 生成 hash 值。③ 用 hash 值对 Nacos 节点数进行取余拿到余数比如 0、1、2、3。④ 根据余数从 Nacos 节点列表中拿到指定的节点地址。我没看懂的点我这里启动了三个 Nacos 节点如下图所示的 三个 Running 节点。但是为什么 Nacos 的 ServersList 会多了一个 192.168.10.197:8848的节点IDEA 启动了三个 nacos 节点nacos 控制台有四个节点4.2 路由转发源码分析入口文件是 DistroFilter.javanaming/src/main/java/com/alibaba/nacos/naming/web/DistroFilter.java请求会先到 DistroFilter 类的 doFilter() 方法拿到正确的节点地址后将请求转发出去。获取需要转发节点地址的代码如下// 找到 Nacos 集群中的目标节点 final String targetServer distroMapper.mapSrv(distroTag); // mapSrv 方法会先 hash然后再取模responsibleTag的值类似这样20.18.7.11:8080 int index distroHash(responsibleTag) % servers.size(); // distroHash 方法里面会对 客户端的 ipport 字符串或者服务名字符串 进行 hash Math.abs(responsibleTag.hashCode() % Integer.MAX_VALUE);不论是自己处理注册请求还是转发给其他节点来处理都会把实例信息存储起来那么是如何进行存储的五、处理请求快到碗里来Nacos 目前有两个版本v1 和 v2如果是 v1则是 instanceController 来处理注册请求否则用 instanceControllerV2。本篇我们只讲解 v1 版本是怎么处理请求的。先上流程图添加实例信息的流程测试用的发起注册的命令curl -X POST http://127.0.0.1:8858/nacos/v1/ns/instance?serviceNamenacos.naming.serviceNameip20.18.7.11port8080核心代码就是这个服务端注册实例的方法首先有一个 synchronized 锁然后执行 put 操作将临时的实例信息存放起来所以重点看下 这个 consistencyService.put() 方法做了什么事情。先看下源码onPut(key, value); // 开启 1s 的延迟任务将数据同步给其他 Nacos 节点 distroProtocol.sync(new DistroKey(key,KeyBuilder.INSTANCE_LIST_KEY_PREFIX),DataOperation.CHANGE, DistroConfig.getInstance().getSyncDelayMillis());这里面做了三件事情① 将实例信息存放到内存缓存 ConcurrentHashMap 里面。② 添加一个任务到 BlockingQueue 队列里面这个任务就是将最新的实例列表通过 UDP 的方式推送给所有客户端服务实例这样客户端就拿到了最新的服务实例列表。没想到吧计算机网络的知识终于用上了~③ 开启 1s 的延迟任务将数据通过给其他 Nacos 节点。注意针对第二点和第三点属于 Distro 一致性协议的一部分里面的内容还比较多我们放到下一讲专门来讲。下一讲知识点预告这里的存储实例和同步的方式和 Eureka 有什么区别Eureka 用的三层缓存架构Nacos 用的 CopyOnWrite 技术。如何推送给所有客户端的UDP 方式。如何同步给 Nacos 其他节点的Distro 一致性协议。六、总结本文通过发起一条注册请求讲解了 Nacos 客户端如何随机选择节点、Nacos Server 如何将请求进行路由转发、Nacos Server 如何存储注册实例。另外本文用到了集群环境关于如何搭建和 debug 集群环境感兴趣的可以留言后续补上这部分的讲解。一条注册请求的核心流程在看、点赞、转发是对我最大的鼓励。
AI写论文必备!这4款AI论文生成工具,让你快速完成职称论文 学术论文智能写作工具指南 在2025年,学术写作正在经历一场智能化的革命。越来越多的人开始尝试使用AI写论文工具来帮助他们撰写学术论文。许多这些工具在撰写硕士、博士等长篇论文时,常常面临一些挑战:它们往往缺乏必要的理论深度࿰… 2026/5/17 11:10:30
2026年廊坊建筑资质申请公司TOP榜出炉,哪家才是正规强中强? 家人们,最近2026年廊坊建筑资质申请公司的TOP榜新鲜出炉啦!对于建筑企业来说,选择一家正规靠谱的资质申请公司至关重要,这关系到企业能不能顺利拿到资质,开展业务。今天咱就来好好唠唠,看看这些上榜的公司里… 2026/7/10 20:01:54
实测对比后!研究生专用降AIGC工具 —— 千笔·专业降AIGC智能体 在AI技术席卷学术写作的今天,越来越多的学生、研究人员和职场人士选择借助AI辅助完成论文、报告和学术材料。然而,随之而来的“AI率超标”问题却成为横亘在学术道路上的隐形障碍——知网、维普、万方等主流查重系统纷纷升级算法,严打AI生成内… 2026/7/9 8:53:58
Keil MDK 5.37+ 手动安装 ARM Compiler 5.06u7:解决STM32 CubeMX项目编译报错 Keil MDK 5.37 手动安装 ARM Compiler 5.06u7 完整指南:从下载到STM32项目编译验证 如果你最近升级到Keil MDK 5.37或更高版本,可能会遇到一个令人头疼的问题:STM32 CubeMX生成的项目无法编译,报错提示"Target uses ARM-Com… 2026/7/12 5:41:15
Cocos Creator游戏开发入门:从零构建打砖块游戏实战教程 1. 项目概述:从零到一,用Cocos Engine构建你的第一个游戏如果你是一名对游戏开发充满好奇,但又对Unity、Unreal Engine这类“庞然大物”望而却步的开发者,或者你是一名希望快速验证玩法、制作轻量级跨平台应用的工程师,… 2026/7/12 5:39:14
DCDC动态决策引擎:自适应AI模型在资源受限场景的精准拦截技术 最近在AI圈子里,一个名为"DCDC克詹世纪盖帽"的项目突然引起了广泛关注。很多开发者第一次看到这个名称时都会感到困惑——这到底是篮球分析工具、体育数据模型,还是某种新型的AI架构?实际上,这个项目代表了AI技术在特定… 2026/7/12 5:39:14
C++控制台电子时钟实战:从时间处理到界面绘制的完整项目指南 1. 项目概述:从零打造一个控制台数字时钟很多C初学者在学完基础语法后,常常会陷入一个迷茫期:书本上的例题都会了,但感觉离“做出一个东西”还很远。这时候,一个目标明确、功能完整的小型实战项目就是最好的突破口。电… 2026/7/12 5:39:14
抽象数据类型 (ADT) 实战:从 C 语言结构体到 Python 类 3 种实现对比 抽象数据类型 (ADT) 实战:从 C 语言结构体到 Python 类 3 种实现对比抽象数据类型(ADT)是计算机科学中描述数据及其操作的数学模型,它独立于具体实现。本文将深入探讨 ADT 的核心概念,并通过 C 语言结构体、Java 类和 … 2026/7/12 5:39:14
今天起,你的IDE将不再“猜”代码:DeepSeek V2.5补全引擎上线即用的4个颠覆性特性(仅限首批白名单用户) 更多请点击: https://kaifayun.com 第一章:DeepSeek V2.5代码补全引擎的发布背景与核心定位 近年来,大模型驱动的智能编程助手正从“通用文本生成”加速迈向“深度理解开发语境”的新阶段。DeepSeek V2.5代码补全引擎正是在这一技术演进与开… 2026/7/12 5:37:13
Git reset 与 revert 深度对比:5个关键差异与 3 种典型应用场景 Git Reset 与 Revert 深度对比:5个关键差异与3种典型应用场景在团队协作开发中,代码版本管理如同行走钢丝——一步失误可能导致整个项目陷入混乱。作为Git进阶用户,你是否曾在深夜面对错误的提交束手无策?是否在强制推送后收到同事… 2026/7/12 0:01:13
GitHub 学生包申请避坑:5个常见失败原因与开发者工具调试方案 GitHub 学生包申请技术排障指南:5个高频失败场景与开发者工具实战方案第一次尝试申请GitHub学生包时,我盯着屏幕上那个不断转圈的加载动画整整15分钟,最终只等来了一行冰冷的错误提示。这可能是许多开发者共同的经历——明明按照教程操作&… 2026/7/12 0:01:13
冒烟测试用例设计规范:5%-10%覆盖率下的3类核心场景与执行标准 冒烟测试用例设计的黄金法则:5%-10%覆盖率下的精准筛选策略在快节奏的敏捷开发环境中,冒烟测试作为质量保障的第一道防线,其重要性不言而喻。当测试资源有限而时间紧迫时,如何从海量测试用例中精准筛选出那关键的5%-10%࿰… 2026/7/12 0:03:14
Git reset 与 revert 深度对比:5个关键差异与 3 种典型应用场景 Git Reset 与 Revert 深度对比:5个关键差异与3种典型应用场景在团队协作开发中,代码版本管理如同行走钢丝——一步失误可能导致整个项目陷入混乱。作为Git进阶用户,你是否曾在深夜面对错误的提交束手无策?是否在强制推送后收到同事… 2026/7/12 0:01:13
GitHub 学生包申请避坑:5个常见失败原因与开发者工具调试方案 GitHub 学生包申请技术排障指南:5个高频失败场景与开发者工具实战方案第一次尝试申请GitHub学生包时,我盯着屏幕上那个不断转圈的加载动画整整15分钟,最终只等来了一行冰冷的错误提示。这可能是许多开发者共同的经历——明明按照教程操作&… 2026/7/12 0:01:13
冒烟测试用例设计规范:5%-10%覆盖率下的3类核心场景与执行标准 冒烟测试用例设计的黄金法则:5%-10%覆盖率下的精准筛选策略在快节奏的敏捷开发环境中,冒烟测试作为质量保障的第一道防线,其重要性不言而喻。当测试资源有限而时间紧迫时,如何从海量测试用例中精准筛选出那关键的5%-10%࿰… 2026/7/12 0:03:14