SpringCloud Alibaba Nacos注册中心源码浅析
一、前置了解
1.1 简介
Nacos是一款阿里巴巴推出的一款微服务发现、配置管理框架。我们本次对将对它的服务注册发现功能进行简单源码分析。
1.2 流程
Nacos的分析分为两部分,一部分是我们的客户端(将自己注册到Nacos),另一部分是Nacos Server处理我们的注册请求等。
1.3 要分析demo示例
细节篇幅不多展示,大致如下
1.3.1 客户端方面:
引入了pom依赖
并在application.yml配置好nacos地址(本地),我们的这个应用启动后会向Nacos服务端去注册。
1.3.2 Nacos服务端方面
我们从https://github.com/alibaba/nacos,即Nacos的官网github按tag拉下源码到本地。
会有很多模块:address、api、client、cmdb、core、console等等。
从console里的Nacos.java文件启动即可,它是个SpringBoot应用,启动后就可以处理注册等请求了。
二、Nacos客户端源码流程
2.1 自动配置触发逻辑入口
打开客户端引入的依赖包的pom,只引入了spring-cloud-alibaba-nacos-discovery:
SpringCloud系列都是通过spring.factories文件进行自动配置,我们打开spring-cloud-alibaba-nacos-discovery的spring.factories文件:
去看看NacosDiscoveryAutoConfiguration这个名字的,名字可以看出它是和自动注册发现相关的配置类:
注册了三个Bean,各个Bean名字也是见名知义,上面两个是服务与nacos注册逻辑本身,最后一个Auto的才是自动配置相关的,应该是入口。
打开NacosAutoServiceRegistration源码,会发现它的父类AbstractAutoServiceRegistration实现了ApplicationListener
注册入口应该就是这里,bind方法开始执行nacos自己的逻辑,bind方法:
start:
这里就可以发现自动配置触发的注册方法了,register();,后续就是如何注册了!
2.2 客户端注册逻辑 register()
不断跟进刚刚的多个register()重名方法,会来到真正的register方法,如下:
逻辑比较直接,主要是获取服务id(比如服务名啥的)+这个实例的具体信息(封装成Instance),最后通过namingService去注册,跟进注册:
心跳机制
其实这里可以看出如果不是临时节点是不需要发送心跳消息的,这里心跳机制是通过beatReactor.addBeatInfo里内部的一个定时任务去实现的,核心就是内部的:
通过线程池跑任务,定时访问Nacos服务端的/instance/beat接口,发送HTTP请求 表示自己活着
继续看注册
刚刚registerInstance里的
继续跟进:
其实就是准备参数准备发http请求了哈,注册的接口地址是NACOS_URL_INSTANCE,也就是:/instance的post请求
客户端注册总结:
1.通过SpringCloud一贯使用的spring.factories文件进行自动配置
2.自动配置类将自己注入IOC容器,并实现了ApplicationListener接口,在web容器初始化事件发布之后加载自己的逻辑
3.加载注册逻辑,通过发送http请求到/instance接口将本身的信息发给Nacos服务端,以及心跳任务定时发送,告诉自己活着
三、Nacos服务端处理注册
上面有说到nacos客户端注册是通过发送http请求到/instance接口。我们看看/instance接口做了什么。Nacos服务端的controller源码如下:
跟进里面的serviceManager.registerInstance注册方法:
createEmptyService是要在放入instance实例(即注册的那个节点信息)之前确保service存在,不存在则创建一个,之后就可以通过getService取出来了。最后再通过addInstance继续注册
看看createEmptyService是怎么创建的,什么结构?
3.1 createEmptyService创建保证Service
通过断点不断跟进createEmptyService方法源码,会来到ServiceManager.java的putService方法:
最后是放到到一个serviceMap的Map结构去了,如下:
双层Map,内部含义其实是:
实际上放入map之后,还会把service初始化,调用init方法,内部会执行健康检查:
1.某个实例超过15秒没收到心跳则把它的healthy属性设置为false
2.继续超过30秒没收到心跳就会直接剔除这个实例
3.2 addInstance注册
回到前面的注册地方,最后保证了有Service之后继续走主逻辑,addInstance:
跟进
最后是执行consistencyService.put(key, instances);注册,这里会有两个实现DistroConsistencyServiceImpl和RaftConsistencyServiceImpl,分别对应着注册中心的AP实现和CP实现,一个基于内存优先可用性(A),一个基于磁盘优先一致性(C),是CAP理论里的取舍。CAP具体可看:https://baike.baidu.com/item/CAP原则/5712863?fr=aladdin
四、Nacos服务端AP模式实现:DistroConsistencyServiceImpl
Nacos的AP模式采用distro协议,Distro是阿里的自创协议,Distro 协议被定位为 临时数据的一致性协议
继续看之前的源码,注册最后是来到:
跟进:
如加的注释这样,分了两步实现
4.1 onPut将注册实例更新到内存注册表
跟进onPut源码:
这里也看到了最后notifier.addTask运用了生产者消费者的思想,里面是添加一个任务到阻塞队列中去,等着处理,因为这些操作本身不需要立即返回成功,对提升性能有很大帮助。
传了ApplyAction.CHANGE类型,我们跟进notifier.addTask,会发现是在Notifier内部类里,它是多线程Runnable的实现类,逻辑都在run方法里,等着对应的线程调起执行。
判断是刚才我们传的ApplyAction.CHANGE会去执行listener.onChange,这里有多个实现,我们可以通过打断点进入的是com.alibaba.nacos.naming.core.Service类中
核心就是updateIPs:
为了防止读写并发冲突,方法第一句直接创建了一个新的HashMap,然后去操作新的HashMap,操作完了之后再去替换老的Map数据,CopyOnWrite的思想。
Eureka防止读写冲突用的是多级缓存结构,多级缓存定时同步,客户端感知及时性不如Nacos。
最后还发布了服务变化事件
4.2 同步实例信息到Nacos Server集群其它节点
回到之前的代码,put方法中是taskDispatcher.addTask(key);进行同步信息到集群其它节点,跟进代码:
就是把节点的key加入到阻塞队列中了,等待之后执行,这是内部类TaskScheduler里的方法,看看整体:
可以看到if (dataSize == partitionConfig.getBatchSyncKeyCount() ||
(System.currentTimeMillis() - lastDispatchTime) > partitionConfig.getTaskDispatchPeriod())
达到一定是数量或时间差,就开始提交批量发送同步任务。逻辑在同步类DataSyncer的run方法里,里面就是往/distro/datum接口发送数据同步。
五、Nacos服务端CP模式实现:RaftConsistencyServiceImpl
Nacos主要是AP模式,CP模式的RaftConsistencyServiceImpl具体就不展开了,这里只简单介绍一下大概实现方式:
1.是阿里自己实现的CP模式的简单raft协议
2.判断自己是Leader节点的话才执行逻辑,否则转发给Leader
3.同步更新实例数据到磁盘,异步更新内存注册表
4.用CountDownLatch实现,必须集群半数以上节点写入成功才返回客户端成功
5.成功后调用/raft/datum/commit接口提交
六、服务发现
客户端通过调用/instance/list接口获取服务端map相关数据,并且会有个延时执行的定时任务去不断更新最新服务数据
__EOF__

本文链接:https://www.cnblogs.com/chz-blogs/p/14325288.html
关于博主:评论和私信会在第一时间回复。或者直接私信我。
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角【推荐】一下。您的鼓励是博主的最大动力!
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?
· 【译】Visual Studio 中新的强大生产力特性
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 【设计模式】告别冗长if-else语句:使用策略模式优化代码结构