Nacos高级特性Raft算法以及原理和源码分析

对比springcloud-config配置中心

最初的springcloud微服务架构采用的配置中心是config 用过的都知道 界面不够优化并且性能没有那么好 已经很少公司使用 nacos架构上跟config有一些相似.

springcloud-config工作原理

在这里插入图片描述
1. 提交配置触发post请求给server端的bus/refresh接口
2. server端接收到请求并发送给Spring Cloud Bus总线
3. Spring Cloud bus接到消息并通知给其它连接到总线的客户端
4. 其它客户端接收到通知,请求Server端获取最新配置
5. 全部客户端均获取到最新的配置

Nacos的工作原理图

在这里插入图片描述
1.客户端发起长轮询请求 服务端接到请求后比较缓存中的数据是否一致,如果不一致则直接返回并更新缓存,如果一致,则通过线程池延迟30s之后再次进行比较

2.为了保证30s的数据变化能够通知到客户端,服务端采用事件订阅的方式来监听配置数据的变化,一旦收到事件那么将触发通知把结果推给客户端完成一次数据推送

springcloud-config和Nacos对比

Nacos Config 无需消息总线系统,系统搭建成本与复杂度比 Spring Cloud Config 低很多。

Nacos Config可以说是高内聚,低耦合

Nacos Config 不会一窝蜂向配置中心索要配置信息,Nacos Config是定点更新。

Nacos Config 有远程配置更新后,会自动更新到 client。其采用了长轮询 Pull 模型。而Spring
Cloud Config 则需要 client 提交请求

长轮训Pull模型

什么是长轮询pull模型

1.配置中心Server和客户端Client实现配置的动态感知一般无外乎两种办法:
一种是Client发起,叫pull模式
一种是Server发起,叫push模式

2.长轮询中Push模式和Pull模式的对比

若使用 Push 模型,需要在 Server 与 Client 间通过心跳机制维护一个长连接。这个长连接的维护成本是比较高的。其适合于 Client 数量不多,且 Server 端数据变化较频繁的场景。优点:数据更新很及时

若使用 Pull 模型,其无需维护长连接,但其实时性不好。因为客户端一定是采用定时间隔去拉取数据。

nacos中的长轮训pull模型改进版本:

1.Nacos 采用的是长轮询机制的 Pull 模型,但不单纯是Pull模型。
是一个HttpPost的长轮询,过期时间默认是30S。
Nacos 长轮询 Pull 模型融合了 Push 与 Pull 模型的优势。

2. 保留了pull
Client 仍定时发起 Pull 请求,查看 Server 端数据是否更新。
若发生了更新,则 Server 立即将更新数据以响应的形式发送给 Client 端;

3.保留了push
若没有发生更新,Server 端并不立即向 Client 返回响应,而是临时性的保持住这个连接一段时间【29.5s】若在此时间段内,Server 端数据发生了变更,则立即将变更数据返回给 Client【这里是动态感知的】若仍未发生变更,则放弃这个连接。等待着下一次 Client 的 Pull 请求。
长轮询 Pull 模型,是 Push 与 Pull 模型的整合,既降低了 Push 模型中长连接的维护问题,又降低了Push 模型实时性较低的问题。

在这里插入图片描述

nacos集群如何保证数据的最终一致性?

nacos默认是ap模式。nacos集群允许数据的短暂不一致性,满足其高可用的特性,那么是如何实现的呢?

nacos底层原理:raft算法

注意:一致性Raft算法 raft是一个最终一致性算法。不是强一致性算法。

是不是基本上一致性都是采用raft算法?不是,Nacos里面就有很多种
一致性算法,就是一个接口的实现类!

扩展:Zookeeper是CP的,允许服务出现不可用的状态,但是要保证数据强一致性 CAP理论面试常考!!

NacosAP和CP模式互相切换

默认是AP的 可以调整 提交PUT请求

http://localhost:8848/nacos/v1/ns/operator/switches?entry=ServerMode&value=CP

Raft 算法

介绍

Raft算法是一种非常易于理解的分布式共识算法。在理解与性能方面等同于业界公认的老牌复杂的paxos共识算法。注意 Raft是一种保证最终一致性的算法,不是强一致性算法。这是一种通过对日志进行管理来达到一致性的算法。
有难度 需要多理解!!不要放弃 这个raft相比于Paxos已经是非常好理解的了 这是简化版的paxos共识算法

Raft算法动画演示:http://thesecretlivesofdata.com/raft/

Raft算法中的几个角色

1.leader(领导):唯一一个负责处理客户端写请求的节点。也可以处理读的请求,同时负责日志复制工作

2.condidate(候选人):参与Leader选举的候选人,有可能会成为Leader的角色

3.follower(跟随者):可以处理客户端的读请求。负责同步来自于Leader的日志。当收到其他候选人的投票请求后进行投票。当发现Leader挂了,其会转变成候选人从而发起Leader选举

4.term(任期/年号 这个不是角色 是角色中的一个重要属性):古代皇帝上位都会给自己封一个年号,比如武帝上位的年号是永初。在Raft算法中对term的变化是+1,以便解决选举过程中的一些复杂性问题。可以通过term年号的大小新旧来判断节点的操作。

算法过程几个重要的点

1.成为候选人的条件:
当follower在心跳超时的范围内没接收到Leader的心跳,此时就会认为Leader挂了。此时会先将自己的term+1 然后会执行以下三个步骤

  • 1.1此时如果收到了其他candidate的投票请求(此时还是follower),那么会将选票投给这个candidate。如果没有收到其他的投票请求,则自己由follower转变为candidate角色。然后term+1
  • 1.2.如果之前没有给别人投票,那么此时给自己投一票
  • 1.3.向其他节点发出投票请求,等待响应

2.跟随者follower如何投票?
follower在接收到投票请求后,会根据以下两个情况来判断是否投票

  • a.若当前该follower的票还没投出去,并且收到的投票请求发起方的节点的term要大于当前该follower,那么就会把票投给该candidate
  • b.如果当前follower节点收到多个candidate的请求,那么采取先到先得的方式进行投票。(前提和a条件一样 term必须要大于当前follower节点)

3.等待响应
当一个candidate发起投票请求后,会等待其他节点的响应结果。有以下三种情况

  • a.收到半数选票的candidate【有可能】成为新的leader。然后将该消息广播给其他节点,并包含了自己的term年号
  • b.接收到新的canditade发来的新leader通知,比较了新leader的term年号比当前节点的大,则当前节点转变为follower
  • c.经过一段时间没有收到半票也没有新的leader发广播,那么进行新一轮的选举

4.票数相同如何处理
票数相同是无法选举出Leader的
这种情况会让这几个票数相同的candidate再次进行一次选举,但是发起选举的时间不一样,此时就会有一个先发起选举的candidate大几率将成为新的leader。

数据写入流程:
当leader节点接收到client的写操作请求时,leader对该请求进行日志记录索引等操作后,广播给其他节点,当集群内过半的节点收到该广播并响应结果,此时Leader将client的这次请求响应回去.此时client就认为这个写的操作已经成功了.但此时还没有真正做修改. 此时leader开始对真实数据进行修改 修改完毕后将结果广播给其他节点 其他节点收到后 通过日志去把最新的数据同步到自己的内存中 实现了数据同步
在这里插入图片描述

Raft算法下脑裂的情况

什么是脑裂?

学名叫网络分区
在一个高可用系统中,当联系着的节点断开联系时,本来为一个整体的系统,分裂成两个独立节
点,两个节点开始争抢共享资源造成系统混乱、数据损坏的现象,成为“脑裂”。也称为网络分区。
Raft 集群存在脑裂问题。在多机房部署中,由于网络连接问题,很容易形成多个分区。而多分区的形
成,很容易产生脑裂,从而导致数据不一致。由于三机房部署的容灾能力最强,所以生产环境下,三机房部署是最为常见的。下面以三机房三节点部署为例进行分析,根据机房断网情况,可以分为五种

例子说明

以下的例子以3节点3机房的情况描述脑裂问题 ABC三节点 A是Leader节点 BC都是follower节点

情况1

会出现脑裂问题吗? 会

脑裂问题产生过程描述:

A和B之前的链接由于网络不通断开了。但是A和C之间的网络是相通的。
此时B节点觉得A节点Leader挂了。此时B节点将自己的年号term增加了1 并且发起了一轮新的Leader选举并且自己成为了候选人的角色candidate。此时A和B之间是互相感知不到的。C收到了B的新一轮leader选举请求,由于B的term是最大的,因此C只能选择投票给B。并且此时C没有办法成为候选人的角色,原因是C和A始终是相同的,心跳并没有timeout,此时B节点收到了C节点的投票,成为了新的leader,但是此时A也是leader。此时就出现了脑裂的问题。A节点由于写请求无法获取到过半节点响应,因此不能完成写操作。B节点是可以获取到过半响应的(BC构成了一个集群),因此可以完成写操作。此时就出现了数据不一致的问题。

关键判断点:
1.成为候选人:B会成为候选人。但是C不会,因为成为候选人的条件是连接leader出现timeout。A和C始终是想通的因此不会成为候选人更加不会成为leader

2.follower投票:C会不会给B投票呢?肯定是会的,因为C接到了更大的年号term的投票请求

3.在一个集群中写得操作需要得到半数节点响应才能成功
在这里插入图片描述

情况2:

会出现脑裂问题吗? 会

这个情况和情况1是差不多的。 会出现脑裂问题

B和C也会选出一个节点出来

在这里插入图片描述

情况3

会出现脑裂问题吗? 不会

原因:C节点不会成为候选人,因为AC是想通的。B节点会成为候选人角色,但是它永远不可能成为主节点,因为他跟A或者C都是不通的,没有办法获取投票,因此不可能成为leader。那么这个集群中A还是作为主节点。
在这里插入图片描述

情况4:

会出现脑裂问题吗? 不会

AB和AC始终是想通的。B和C没有机会成为候选人角色。
在这里插入图片描述

情况5

会出现脑裂问题吗? 不会

B和C都有可能成为候选人,但是永远获取不到投票,没有办法成为leader
在这里插入图片描述

Raft算法下如何解决脑裂问题下的数据最终一致性?

1.日志索引
2.任期

这里不展开细讲 以后有时间单独整理一篇文章
在这里插入图片描述

Leader宕机 对集群数据一致性有影响吗?

问题描述

client发送一个写得请求到了nacos集群中 leader宕机了 对集群中的数据会产生影响吗?
在这里插入图片描述

情况1:

写请求还没到达leader,leader就挂了

会保证数据一致性吗? 会

解析:因为这个请求在这个集群中压根就没有存在过,不会对一致性有任何影响


情况2

写请求到达leader,leader在同步数据给其他节点的时候挂了

会保证数据一致性吗? 会

解析: 这个请求一开始到旧leader,由于旧leader挂了,这个写得操作还没来得及同步就挂了,而client此时没有收到旧leader的成功处理响应(可以理解是一个ack的机制)于是再次请求发起写得请求,但是第二次这个请求到集群时,新的leader已经出现了,此时新的leader会进行同步数据并把结果响应给client完成一次写操作
当client发送写得操作给leader,还没同步给其他节点就挂了,此时其他两个节点会产生新的leader,前任leader重启之后会作为follower的身份重新加入集群并同步新的leader的数据从而保证数据的最终一致性。


情况3

client发送写请求给leader,leader接受到后开始向follower复制数据。在部分follower复制完之后leader挂了。(有可能过半、也有可能不过半、也有可能一个都没复制完) 。此时client等待响应结果。

会保证数据一致性吗? 会

解析:
此时另外两个节点会产生一个新的leader,以下两种情况
(1):如果新leader产生于已经复制过同步数据日志的follower(新leader节点收到过旧leader挂之前发出的复同步数据请求),那么新的leader会把这个未完成的工作进行完成,并返回结果给client进行ack签收。会保证数据的最终一致性

(2):如果新leader产生于没有复制过同步数据日志的follower(新leader没有收到过旧leader挂之前发出的复制同步数据请求),会把这个日志请求放弃,此时client会重新发送该请求,新leader收到后会进行同步,从而保证数据的最终一致性。


情况4:

比较难理解

讲这个之前先回忆一下,raft算法中同步数据的步骤,leader收到请求之后会发起同步数据的请求到其他的节点,当有过半数的节点对其进行响应后(此时只是对请求做回复 follower节点此时并不会对数据做真实的修改),leader就会对这次请求做一个结果响应到client,这时候client就知道,我这次写得请求发送成功了。最后一个步骤,leader会进行广播本次修改的数据到底怎么修改。

当client发送写得请求到leader,leader吧同步数据的请求发送并且收到过半数的节点响应,client也收到了响应,最后一个步骤进行广播的时候,广播还没发出去,leader挂了。。会对数据的一致性有影响嘛??

答案:raft算法下这种情况下是可以保证数据的最终一致性的。

遵循一个原则:完全领导人原则。 一句话理解,新的领导会把旧的领导没做完的事做完,并且raft算法保证了既然本次请求得到了半数节点的响应,那么证明这个请求是完全合情合理的,此时应该被记录下来(日志提交),当新的leader产生时,需要追溯一下集群中是否有上任领导没干完的活,如果有,新领导需要把这个工作完成,从而保证了数据的最终一致性

Nacos Discovery(注册中心) 原理源码分析

Eureka注册中心架构图

在这里插入图片描述

Registry(注册服务):Client会发送一个rest请求到eureka服务端,并把自身的信息带过去 比如ip 端口元数据等.服务端收到请求后把元数据存储在s双层map中


Renew(服务续约):心跳/续约. 服务注册后client会维护一个心跳来持续通知Eureka服务端.说明本服务处于可用状态方式被剔除.默认30s发送一次心跳


Get Registry(获取注册列表):消费者启动会发送一个rest请求到eureka服务端获取注册中心的服务提供者清单并缓存到本地.同时为了性能安全性考虑,Eureka会每隔30s更新缓存中的清单


Make Remote Call(服务调用):根据客户端通过注册中心存放的提供者地址进行远程调用 通常是http 客户端使用feign


Cancel(下线通知):当client需要关闭或者重启时.会提前发送一个rest请求给eureka服务端.收到请求后会吧该服务状态设置为下线down.并通过事件广播出去


evict(失效剔除):eureka为了有些服务由于网络故障原因等不能提供服务.因此eureka服务端在启动的时候会创建一个定时任务,每隔90s从服务清单内把超时没有续约的服务剔除


注意点:

  • 1.服务注册到不同的erueka
  • 2.eureka集群之间会进行同步数据 一个服务向某一个eureka拿注册列表,拿到的都是同一份数据,这是集群之间实现的数据同步

Nacos架构图

在这里插入图片描述
Nacos Server:nacos集群

pri-raft:Raft一致性方案

config Service:配置中心

Naming Service:注册中心

Naos Core:核心组件

OpenApi:通过OpenAPI暴露

Name Server:消费者通过服务名远程调用提供者

User Console:控制台

MVC架构:分层- User Console-控制台 服务模块: 1.openAPI 2.模块(注册 配置) 3.持久层(raft一致性)

Nacos的Clinet客户端的服务注册与心跳

Nacos 源码分析目标

看源码一定要带问题和目标去看 否则你会发现你很迷茫 你根本不知道你想了解什么 看源码不要没一行都去点 只看核心的!

目标1.理解Nacos和springCloud整合源码分析

  • Spring-cloud-commons依赖以及nacos discovery starter依赖
  • springcoud应用程序入口:监听器接口的触发

参考流程图 找到对应核心的类
在这里插入图片描述

startr坐标里面带了springcloud-commons依赖包,可以让该服务注册到注册中心,怎么做到的???

a.commons包中通过SPI机制找到入口AutoServiceRegistrationAutoConfiguration,这个类只要导入了starter就会通过commons包被注入spring容器。这个类就是完成NacosClient服务注册的自动配置类在这里插入图片描述
b.AutoServiceRegistrationAutoConfiguration中注入了AutoServiceRegistration对象,怎么进来的呢?非常重要。在这里插入图片描述
c.AutoServiceRegistration是个接口,它的实现类是怎么进入spring容器的呢?注意 这个时候不要通过这个接口找实现类 因为此时点过去你依然无法确定这个类是什么时候被加载到spring容器管理的

在这里插入图片描述

d.此时需要搞清楚这个问题,需要去starter包中去寻找答案。通过spring-cloud-starter-alibaba-nacos-config的spi中找到NacosServiceRegistryAutoConfiguration
在这里插入图片描述

e.其中有个Bean叫做NacosAutoServiceRegistration,然后通过它注入。查看一下它的类图。 AutoServiceRegistration的实现类。发现NacosAutoServiceRegistration有个抽象父类AbstractAutoServiceRegistration,这个抽象父类实现了AutoServiceRegistration。也就是说其实AutoServiceRegistration接口注入的实现类就是这个NacosAutoServiceRegistration 至此完成闭环。

在这里插入图片描述

类图:

在这里插入图片描述

监听器接口的触发

AbstractAutoServiceRegistration实现了spring的监听器,它是NacosAuthServiceRegistration的抽象父类,看他的监听器的实现方法onApplicationEvent方法。Tomcat启动会触发这个监听器开始工作。

这里需要了解spring的事件发布机制 非常简单

总结:Nacos和springcloud整合的关键在于.通过starter包注入相关的依赖对象.这个对象必须拥有一个可被触发的点.那么就是通过spring的发布event事件的机制.感兴趣的可以去搜索一下 其实就是所谓的观察者模式.那么这个事件在springboot启动的时候发布,那么监听器在这个starter包中的类中,当我们引入依赖,就会触发这个监听器对这个事件做处理.不得不说设计得很妙

看到这里 源码已经用到了两种我们常用的设计模式 1.模板方法 注入对象不直接实现接口 而是由父类去实现 2.观察者模式 spring的事件发布机制
在这里插入图片描述

目标2.客户端client注册流程和心跳的流程

流程图:
在这里插入图片描述
注意:看源码只需要看那些关键的动作 比如 doXX registerXXX getInstance 一路找到NcosServiceRegistry这个类
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
根据最后一张图我们看到有两个核心的方法
beatReactor.addBeatInfo:这个一看就是发送心跳的方法
serverProxy.registerService:服务client注册

总结:当springboot程序启动后,会触发bind事件.监听器会处理客户端client注册到nacos的流程.其中包括了服务注册和发送心跳. 接下来我们详细了解下这两个方法内部做了哪些事情.一定要结合图来看!!

服务注册

这个代码相信你也一定写过
1.组装请求参数
2.发起http请求(RestTemplate okHttp httpClient …)

此时回过头去看一下nacos的架构图就能看到.nacos server服务端的所有操作都是通过Open-API来提供支持的 链接:https://nacos.io/zh-cn/docs/open-api.html 聪明的小伙伴应该知道 那一定会有controller入口 此时我们只需要知道 服务组装了一些自己的参数ip port group name等等然后发送一个post请求提交给nacos 此时就完成了注册的服务
在这里插入图片描述
服务心跳
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
总结: 熟悉的味道 又是发起了一次http请求 将自己的信息提交给nacos告诉它 我很好 我还活着!

目标3 理解Nacos对httpClient的封装

为什么需要封装httpClient? 因为nacos的架构非常庞大 所有的服务端模块的服务都需要通过openapi暴露.那么需要定义一套nacos自己的http规范 比如独有的响应题 请求头 这样提高了效率 并且最重要的是 高大上!像很多框架都会这么做 比如分布式事务框架seata也是封装了restTemlate.

在这里插入图片描述
在这里插入图片描述在这里插入图片描述在这里插入图片描述

运行服务端源码

1.源码下载 选择client-1.3.3版本

在这里插入图片描述

2.使用idea打开
在这里插入图片描述
3.修改配置

3.1修改application.propertie存储数据到mysq
在这里插入图片描述
3.2 启动参数增加 使用单节点启动 否则报错(默认启动集群模式)
在这里插入图片描述
4.启动
在这里插入图片描述

Nacos服务端注册与心跳源码

首先搞个服务启动 在控制器打个断点在这里插入图片描述

主要动作:
重点看ServiceManager这个类的操作在这里插入图片描述

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值