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

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

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

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

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

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

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 Server的启动脚本

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

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

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

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

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

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

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

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

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

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

这里面有个

NacosDiscoveryAutoConfiguration

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

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

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

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

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

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

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

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

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

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

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

还有register;

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

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

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

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

我们在这里debug看下:

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

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


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

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

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

看这个方法registerInstance

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

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

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

Map<String, Map<String, Service>> serviceMap;---注册表结构---结构是
Map<public, Map<DEFAULT_GROUP@@provider, Map<String, Set<Instance>>>>

provider是我的服务名字!

再回到putServiceAndInit方法看init

里面有个参数clientBeatCheckTask

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

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

回到这个类ClientBeatCheckTast看到run方法

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

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

进入addInstance方法

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

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

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

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

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

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

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

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

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

继续看DelegateConsistencyServiceImpl.put

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

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

这里判断一定是第一个

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

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

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

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

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

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

什么?这么快结束了?

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

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

看到了take吧?

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

看下有三个实现类

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

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

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

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

当然的确论证了观点

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

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

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

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

Map我们可以再细化下

Map<String, Map<String, Service>> serviceMap; -- 注册表结构现在细化下
Service:Map<String, Cluster> clusterMap;Cluster:
Set<Instance>----所以最后核心注册表结构是:
Map<public, Map<DEFAULT_GROUP@@provider, Map<DEFAULT, Set<Instance>>>>

看下clusterMap

注册我就讲到这里

总结下:

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

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

辑;

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

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


而本节留的几个问题

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

后面一节我会重点剖析!

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

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

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

整体收篇单独总结!

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

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

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

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

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

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

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

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

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

这块代码在这里CopyOnWrite

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

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

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

  1. 微信群控系统源码的实现原理,核心源码实现,核心框架。

    微信群控系统已经应用于各个行业,也成为大家在微信推广营销的重要工具.如今也演变出群控各种手机软件的各种系统.我们听到的主要有微信群控,淘宝群控,陌陌群控,QQ群控等等.下面我们就来简单介绍下群控系统源 ...

  2. SpringCloud Alibaba——精读Nacos+CMDB+核心源码阅读(7w字长篇)

    文章目录 Nacos 1.介绍 2.使用场景 2.1.动态配置服务 2.2.服务发现及管理 2.2.1.服务注册 2.2.2.服务心跳 2.2.3.服务同步 2.2.4.服务发现 3.环境搭建 3.1 ...

  3. RocketMQ源码系列(一) NameServer 核心源码解析

    目录 一.NameServer 介绍 二.NameServer 功能列表 三.NameServer 架构分析 四.NameServer 工程目录解析 五.NameServer 启动流程分析 1)  创 ...

  4. HTML5 2D游戏引擎研发系列 第一章

     HI,大家好,我是白泽,一名游戏设计师,一直专注各平台的2D游戏引擎研发,HTML5是我准备进入的新领域,我有个习惯,刚接触的新领域我都会习惯自己写一套游戏引擎,而不用第三方提供的,为了方便自己学习 ...

  5. 面试官系统精讲Java源码及大厂真题 - 09 TreeMap 和 LinkedHashMap 核心源码解析

    09 TreeMap 和 LinkedHashMap 核心源码解析 更新时间:2019-09-05 10:15:03 人的影响短暂而微弱,书的影响则广泛而深远. --普希金 引导语 在熟悉 HashM ...

  6. 电路-第五版-邱关源-习题解答-第一章

    系列文章目录 电路-第五版-邱关源-习题解答-第一章 文章目录 系列文章目录 1-1 1-2 1-3 1-4 1-5 1-6 1-7 1-8 1-9 1-10 1-11 1-12 1-13 1-14 ...

  7. Android框架源码分析——从设计模式角度看 Retrofit 核心源码

    Android框架源码分析--从设计模式角度看 Retrofit 核心源码 Retrofit中用到了许多常见的设计模式:代理模式.外观模式.构建者模式等.我们将从这三种设计模式入手,分析 Retrof ...

  8. WEBGL 2D游戏引擎研发系列 第一章 新的开始

    WEBGL 2D游戏引擎研发系列 第一章 <新的开始> ~\(≥▽≤)/~HTML5游戏开发者社区(群号:326492427) 转载请注明出处:http://html5gamedev.or ...

  9. 【源码阅读计划】浅析 Java 线程池工作原理及核心源码

    [源码阅读计划]浅析 Java 线程池工作原理及核心源码 为什么要用线程池? 线程池的设计 线程池如何维护自身状态? 线程池如何管理任务? execute函数执行过程(分配) getTask 函数(获 ...

最新文章

  1. 用secureCRT操作ubuntu终端
  2. Asp.net MVC 学习之路-002
  3. Bootloader
  4. svn: Checksum mismatch while updating 'D:\workspace\demo\test\.svn\text-base\test.php.svn-base'
  5. 怎么看Windows11系统是激活的 Windows11检查激活状态方法
  6. sql中exits和in的区别
  7. 2018-1-2Linux基础知识(19)vi编辑器及bash算数
  8. ubuntu 安装 virt-manager 虚拟机
  9. 监控摄像头GB28181转RTMP、FLV、HLS、RTSP多种格式实现网页嵌入播放
  10. TFN推出2.5G传输分析仪D240S 等待您来验证
  11. JSP文件的中文在浏览器上显示乱码解决方法
  12. 液压缸、气缸、电动缸的参数对比
  13. 如何去掉桌面图标快捷方式的小箭头(小技巧)
  14. 互联网快讯:多地要求商家下架槟榔;多所高校延长专硕学制至3年
  15. LTspice基础教程-006.运行仿真与瞬态分析
  16. 用matlab求解线性方程组
  17. The NPF or NPCAP service is not installed, please install Winpcap or Npcap aand reboot的解决方法
  18. JDBC:事物管理与事物隔离界别
  19. 2021-09-10 简单的音乐节奏游戏实现
  20. 零基础学c语言要多久,零基础学习单片机编程需要多长时间?

热门文章

  1. 利用 OpenCV+ConvNets 检测几何图形
  2. 赠书福利 | Tidio AI 趋势报告:约42%受访者能够接受机器人伴侣
  3. 脸书 AI 识别误将黑人标记为「灵长类动物」
  4. 赠书 | JavaScript 武力值飙升!用 TensorFlow.js 轻松在浏览器里搞深度学习
  5. 清明出游,你会“鸽”酒店吗?AI 早已看穿一切
  6. 仅剩一周!!CSDN年终大放血!人人有份的大奖你确定不来?
  7. 认知智能再突破,阿里 18 篇论文入选 AI 顶会 KDD
  8. Google、微软、阿里、腾讯、百度这些大公司在GitHub上开源投入排名分析 | CSDN原力计划...
  9. CS本科毕业生能拿到45万年薪?
  10. 一行代码,解决空指针问题.