Nacos源码系列——第一章(Nacos核心源码主线剖析上)

在讲具体的源码之前,我有几点想说明下,很多开发可能觉得源码不重要,甚至觉得互联网

的知识,目前够用就可以,也不需要多么精通。的确,在大多数的公司中,你能用你的知识

解决问题就可以,不一定非要涉及到源码,但是你们应该知道如果想进大厂的话,对读源码

的能力是非常高的,甚至像阿里,字节这样的厂,面试经常会问到,尤其是做中间件组件,

对开源框架的源码阅读能力,是有一定的要求的,那么想熟悉源码的过程和思想,可以看看

这篇,会让你受益匪浅!!

1、为什么要看源码

我总结有以下几点,个人觉得非常重要的!

  • 提升技术功底

可以学习源码里的优秀设计思想,比如一些疑难问题的解决思路,一些优秀的设计模式,可

整体提升自己的技术功底。

  • 深度掌握技术框架

源码看多了,对于一个新技术或者新框架的掌握速度就会有大幅度的提升,其实市面上的框

架源码本身原理大差不差,在一些细节上有所差异,看下demo就可以大致知道底层的实现。

框架更新再快也不用怕了。

  • 快速定位线上的问题

遇到线上问题,特别是框架里的bug,能够快速定位,相比别人没看过源码,你具有非常大

的优势。

  • 对面试大有好处

面试一线互联网公司对于技术框架,都会问到源码底层的实现。

  • 知其然知其所以然

对技术有追求的人必须做的事情,使用一个好的框架,很想知道底层怎么实现的。

  • 拥抱开源社区

参与到开源项目的开发,结识更多的大牛,积累更多优质人脉。

2、怎么阅读源码

  • 先学会使用

先看官方文档快速掌握框架的基本使用,要多去用,争取自己可以熟悉他的大多数功能,不

要只用一遍,很快就会忘记,先会用,会用!!

  • 抓主线

框架源码下载下来,自己写一个demo,顺腾摸瓜快速的静态看源码,也就是不debug看,就

像读英文那样,边边角角先不管,看主线,画出源码主流程图,切勿一开始就陷入源码的细

枝末节,否则很快把自己就绕晕了;有能力的凭经验猜。

  • 画图做笔记

总结框架的一些核心功能点,从这些功能点入手深入到源码的细节,边看源码边画源码走向

图,并对关键源码的理解做笔记,把源码里的闪光点记录下来,后续借鉴到工作项目中去,

理解能力强的可以直接看静态的源码,也可以边看源码边debug执行,一定要记录关键变量

的值,和你的方法,不然你还是出不来。

  • 整合总结

所有的功能点和源码都分析完了后,回到主流程,重新梳理一遍,争取多来几遍,在自己的

脑瓜子里做个整合。

3、Nacos核心功能点有哪些

  • 服务注册

Nacos Client端会通过发送Rest请求的方式,向Nacos Server端注册自己的服务,提供自身

的元数据,比如ip地址,端口等信息,Nacos Server收到注册信息后,就会把这些元数据信

息存储到一个双层的内存Map中。

  • 服务心跳

在服务注册后,Nacos Client会维护一个定时心跳来持续通知Nacos Server,说明服务一直

处于可用状态,防止被剔除,默认5s发送一次心跳。

  • 服务健康检查

Nacos Server会开启一个定时任务,用来检查注册服务实例的健康情况,对于超过15s没有

收到客户端心跳的实例,会将它的healthy属性置位false(客户端服务发现的时候不会发

现),如果某个实例超过30s没有收到心跳,直接剔除该实例,被踢出的实例如果恢复的话

发送心跳重新注册。

  • 服务发现

Nacos Client端在调用服务提供者提供的服务时,会发送一个Rest请求给Nacos Server,获

取上面注册的服务清单,并且缓存Nacos Client本地,同时会在Nacos Client本地开启一个定

时任务,定时拉取服务端最新的注册表信息,更新到本地缓存。

  • 服务同步

Nacos Server集群之间会互相同步实例,用来保证服务信息的唯一性。

4、开启Nacos阅读源码之路

还是一样的,在读源码之前,一定要先了解Nacos是做什么用的,自己用demo跑一个实例,

不然上来就看源码,根本不行,如果有需要,可以看我前几篇的Nacos整合博客:

Nacos整合Dubbo

废话不多说,我来带你们看下源码的步骤和过程;

Nacos版本是1.4.2,官网自己下载下来,怎么跑我前面也有博客写

Nacos源码搭建

当你构建好了之后,第一个问题来了,这么多的结构,你怎么样可以发现源码的主启动类在

哪里?

Nacos源码系列——第一章(Nacos核心源码主线剖析上)_第1张图片

教大家一个方法怎么入手源码:

从启动脚本入手!

看下Nacos Server的启动脚本

Nacos源码系列——第一章(Nacos核心源码主线剖析上)_第2张图片

这有个xxx.jar,一看就知道要启动这个jar,进target下看看,原来是nacos-server.jar;

那么学过maven的就一定知道,这个名字一定是打包的时候手动加的,因为上面的Nacos的

源码项目里没有叫做nacos-server的jar包,于是我们就可以全局搜下:

Nacos源码系列——第一章(Nacos核心源码主线剖析上)_第3张图片

发现是在console这个项目里,点开看下pom文件

Nacos源码系列——第一章(Nacos核心源码主线剖析上)_第4张图片

那这我就不多说了,直接找到Nacos,启动的时候记得添加参数-Dnacos.standalone=true

我自己手写了两个demo,一个是consumer,一个是provider;

依赖关系:(这三个必须要有)

Nacos源码系列——第一章(Nacos核心源码主线剖析上)_第5张图片

于是我启动我的服务,发现已经注册上去了:

Nacos源码系列——第一章(Nacos核心源码主线剖析上)_第6张图片

源码部分开始,首先我们引入的一个依赖,是spring-cloud-starter-nacos-discovery,我们看

下里面:知道Spring的应该知道这个spring.factory的意思,自动装配机制了解吧。

Nacos源码系列——第一章(Nacos核心源码主线剖析上)_第7张图片

这里面有个

NacosDiscoveryAutoConfiguration

这个类,为什么要看他,一般情况下找这个类的时候,找和pom依赖很类似的

pom依赖是spring-cloud-starter-nacos-discovery,这个类看样子很像。一般比较厉害的程序

员写开源项目还是很厉害的,命名也很规范。

点进去看看,里面有三个Bean

Nacos源码系列——第一章(Nacos核心源码主线剖析上)_第8张图片

这三个bean,优先看带Auto的,Springboot项目叫自动装配,这是个小技巧;那有的程序员

命名很不规范,那没办法了,经验猜不出来,一个一个看吧。。。

而且这类的入参具有上面两个bean对象,所以看样子一定没谁了,是个核心类;

Nacos源码系列——第一章(Nacos核心源码主线剖析上)_第9张图片

我看到了ApplicationListener,就知道Spring容器在启动的时候,要做些事情。

看到if 判断,先跳过,这不是我们关注的主线。

Nacos源码系列——第一章(Nacos核心源码主线剖析上)_第10张图片

看start方法,里面有个register(),看起来像是注册的意思

Nacos源码系列——第一章(Nacos核心源码主线剖析上)_第11张图片

往下走,到实现类里,又有一个register();

Nacos源码系列——第一章(Nacos核心源码主线剖析上)_第12张图片

Nacos源码系列——第一章(Nacos核心源码主线剖析上)_第13张图片

还有register;

Nacos源码系列——第一章(Nacos核心源码主线剖析上)_第14张图片

其他的边边角角我不会带你过,我会直接让你看主线,服务启动的第一件事情,应该是要注

册,所以我看到了有注册的方法就沿着一直走下去。

Nacos源码系列——第一章(Nacos核心源码主线剖析上)_第15张图片

再进去看看,记住一定要抓主线!!

Nacos源码系列——第一章(Nacos核心源码主线剖析上)_第16张图片

发现这里有点像一个http请求,而且这个请求是post,前面都是一些Map去封装的参数,于是

我们在这里debug看下:

Nacos源码系列——第一章(Nacos核心源码主线剖析上)_第17张图片

你发现有个请求,/nacos/v1/ns/instance,去nacos官网搜下这个api

发现真的是服务注册的api,好了到这里,客户端怎么注册到server端的过程,我就先走到这


那么服务端收到这个请求,干了啥呢?怎么处理的呢?

刚刚说客户端/nacos/v1/ns/instance这个请求,一定是往服务端去的,搜下服务端的代码;

Nacos源码系列——第一章(Nacos核心源码主线剖析上)_第18张图片

里面有Post方法,很明显这方法就是服务端处理客户端传来的实例Instance进行注册的;

Nacos源码系列——第一章(Nacos核心源码主线剖析上)_第19张图片

看这个方法registerInstance

Nacos源码系列——第一章(Nacos核心源码主线剖析上)_第20张图片

createEmptyService看样子像是创建一个空的Service,很明显判断是不是为空,不为空就初始化一个Service

Nacos源码系列——第一章(Nacos核心源码主线剖析上)_第21张图片

进入putServiceAndInit放进一个Map中,并初始化,这里用到了DCL

Nacos源码系列——第一章(Nacos核心源码主线剖析上)_第22张图片

实际上我可以提前剧透下这个serviceMap就是我们说的注册表,并且是个双层Map

Map> serviceMap;---注册表结构


---结构是
Map>>>

provider是我的服务名字!

Nacos源码系列——第一章(Nacos核心源码主线剖析上)_第23张图片

再回到putServiceAndInit方法看init

Nacos源码系列——第一章(Nacos核心源码主线剖析上)_第24张图片

里面有个参数clientBeatCheckTask

见明知意,客户端发起心跳检查的任务,很这是不是我们说的每隔5s发起一个心跳

检查任务给服务端呢?带着疑惑去看下(这里先留下困惑,这个5s是什么)

Nacos源码系列——第一章(Nacos核心源码主线剖析上)_第25张图片

回到这个类ClientBeatCheckTast看到run方法

Nacos源码系列——第一章(Nacos核心源码主线剖析上)_第26张图片

这个getInstanceHeartBeatTimeOut方法,点进去看,超过15s后健康状态置false

Nacos源码系列——第一章(Nacos核心源码主线剖析上)_第27张图片

找到这里,发现有点偏离主线了,我们要找注册的逻辑,继续回到主线,注册

进入addInstance方法

Nacos源码系列——第一章(Nacos核心源码主线剖析上)_第28张图片

里面的key = com.alibaba.nacos.naming.iplist.ephemeral.public##DEFAULT_GROUP@@provider

这个key后面要放进一个consistancyService这个map中

命名可以知道,这是持久化表,而ephemeral英文是临时的;

Nacos源码系列——第一章(Nacos核心源码主线剖析上)_第29张图片

我们点下put方法,发现有很多实现类,这个是读源码的一个不好的场景,我不知道应该进哪个,教大家一个技巧,当你不知道的时候,凭经验猜

猜不出来,就在这个点打个断点

Nacos源码系列——第一章(Nacos核心源码主线剖析上)_第30张图片

如果猜的话,看下这个源头来自哪里

这里有个Delegate,我凭经验猜就是实现类DelegateConsistencyServiceImpl,事实证明我是对的,这个key实际上就是我上面发的,判断这个key是不是包含临时标志ephemeral

ephemeralConsistencyService:临时实例的注册
persistentConsistencyService:持久实例的注册

这两个再剧透下,区分Ap和Cp架构的

继续看DelegateConsistencyServiceImpl.put

Nacos源码系列——第一章(Nacos核心源码主线剖析上)_第31张图片

又是一堆实现类,还是技巧,要么经验,要么断点,这再教一个

mapConsistencyService(key)这个返回的是什么类

这里判断一定是第一个

进入ephemeralConsistencyService的实现类,只有一个

Nacos源码系列——第一章(Nacos核心源码主线剖析上)_第32张图片

现在知道这个put应该进入哪里了吧,进去看看进到onput

Nacos源码系列——第一章(Nacos核心源码主线剖析上)_第33张图片

这个dataMap先不管,混个眼熟,后面看多了自然穿起来

进到addTask,分支代码先暂时忽略

Nacos源码系列——第一章(Nacos核心源码主线剖析上)_第34张图片

看下这个offer是什么,很明显是阻塞队列,一个内存队列,把客户端传来的参数,封装了后

传到了这个阻塞队列里去,Ok,注册逻辑到此结束!!

什么?这么快结束了?

刚刚不是说有个注册表是个双层Map吗,客户端信息没有写到双层Map里去啊?

知道阻塞队列,应该知道既然往里塞,就一定会异步去取出来,你往下稍微走走看

看到了take吧?

Nacos源码系列——第一章(Nacos核心源码主线剖析上)_第35张图片

再看看handle方法,分支不看,大概过一遍,这里很有可能是拿到队列里的注册信息去做事情,

Nacos源码系列——第一章(Nacos核心源码主线剖析上)_第36张图片

看下有三个实现类

Nacos源码系列——第一章(Nacos核心源码主线剖析上)_第37张图片

我这里其实都看了下,发现是第一个,为啥,你会发现其他两个里面都是consistency持久化,我们一直在看的是ephemeral,而且断点进去的,和我猜想的结果是一样的

Nacos源码系列——第一章(Nacos核心源码主线剖析上)_第38张图片

边边角角的逻辑先过掉,真正的注册的逻辑在这里

Nacos源码系列——第一章(Nacos核心源码主线剖析上)_第39张图片

这么多代码大致看了下,我找了半天,没什么技巧了,是在这个方法里做了注册

Nacos源码系列——第一章(Nacos核心源码主线剖析上)_第40张图片

进去看下,传入参数ephemeral是true还是false,默认是true,而到现在这个值一直没变过

Nacos源码系列——第一章(Nacos核心源码主线剖析上)_第41张图片

当然的确论证了观点

Nacos源码系列——第一章(Nacos核心源码主线剖析上)_第42张图片

中间一大段逻辑,先不管,最终会写到这里来,这里就是注册表的最核心的地方

Nacos源码系列——第一章(Nacos核心源码主线剖析上)_第43张图片

哎,这个和我上面说的好像不一样啊

我们看下Service里面是什么,有个Map对象

Nacos源码系列——第一章(Nacos核心源码主线剖析上)_第44张图片

里面有个Cluster,现在知道这个双层Map有多复杂了吧

Nacos源码系列——第一章(Nacos核心源码主线剖析上)_第45张图片

Map我们可以再细化下

Map> serviceMap; -- 注册表结构

现在细化下
Service:

Map clusterMap;

Cluster:
Set

----所以最后核心注册表结构是:
Map>>>

看下clusterMap

Nacos源码系列——第一章(Nacos核心源码主线剖析上)_第46张图片

注册我就讲到这里

总结下:

客户端启动的时候,发起http请求,发送一些注册参数,服务端会开启一个线程把这些参数

放在一个阻塞队列里,并异步的消费去把这些放在一个双层Map中的Set集合,实现注册的逻

辑;

那你怎么知道哪里去启动这个线程呢?

回到刚刚的逻辑,很明显这里被Spring初始化的时候调用,里面就是线程池的逻辑我就不看了。

Nacos源码系列——第一章(Nacos核心源码主线剖析上)_第47张图片


而本节留的几个问题

  • 客户端怎么定时发心跳的?多久发一次?

后面一节我会重点剖析!

  • 为什么要用异步去注册,而不用同步?

设想下如果这个中间件,采用同步注册,如果运维启动一批服务注册上去,先注册,再消费队列,每个服务的启动时间是不是很久,如果我的一个项目中引入很多很多中间件,每个中间件都要同步去做这些事情,那整个系统启动非常慢导致不可用。思想我觉得应该都理解

  • 阿里的开发人员为什么要这么设计?我们有没有什么值得学习的地方?

整体收篇单独总结!

  • 注册表的设计如何防止多节点读写并发冲突?

重点来了,为什么要这么设计双层Map呢?

1、考虑到目前开发的环境,和市面上公司的情况,有的公司钱不多,不能支撑每个环境都做一套注册中心;Nacos支持你部署一套环境,支持你所有的开发环境,区分namespace和group。

2、高可扩展,大型互联网公司,一定是多机房部署,比如深圳机房,华南机房,不可能我只有一个机房在北京,内蒙古那边访问个淘宝要等很久才出来?所以双层Map中会有Cluster,通过Cluster区分哪个集群属于哪个机房部署;这种商用中间件一定是这样多扩展的。

Nacos源码系列——第一章(Nacos核心源码主线剖析上)_第48张图片

3、这个注册表会不停的修改,那么其他服务拉取这个注册表的时候,保证数据怎么正确?

采用CopyOnWrite思想,我们知道注册的逻辑比较复杂,很多步骤,每一步都可能会改这个注册表的结构和逻辑,我们不可能加锁,性能效率会非常低而且并发很低,读写冲突问题,我们采用写时复制思想;阿里这样的中间件不会随随便便加把锁,所以在写的时候,修改的是一份副本,然后在替换注册表,读的时候是读真正替换后的注册表!!

等于是读写分离,但是有个弊端,你写你的,我读我的,有可能会导致数据不一致,只有当替换回来的时候,我才能读到新的数据。

虽然写时复制提高了我们的并发了,但是对数据的实时性就不能很好的保证,那么阿里怎么处理这个呢?但是这个影响大吗,其实并不大,无非就是生产者启动慢点罢了,延迟一点的感知其实对整个系统的影响并不大,Eureka都延迟几十秒,Nacos这个延迟并不大,后面我会说到客户端也会定时拉取服务端最新的注册信息,以及剔除下线的服务,目前大大的提升了并发,总不能又要高并发,又要实时感知及时。

当然也不存在每个服务都复制一份去写,因为Server后台就一个线程去取队列注册,不存在多个线程去对不同的服务进行写时复制。

这块代码在这里CopyOnWrite

Nacos源码系列——第一章(Nacos核心源码主线剖析上)_第49张图片

Nacos源码系列——第一章(Nacos核心源码主线剖析上)_第50张图片

总结的思维脑图:https://www.processon.com/view/link/60d87a95637689326ce6a928

好了,目前注册就讲到这里,总结的思维脑图在下一章节会发出来,欢迎指正!!

你可能感兴趣的:(Dubbo微服务专题,java)