文章目录
1.什么是Nacos
官方:一个更易于构建云原生应用的动态服务发现( Nacos Discovery )、服务配置( Nacos Config )和服务管理平台
集 注册中心+配置中心+服务管理 平台
注册中心将所有服务进行注册,后续只需要根据服务名进行服务的调用
配置中心将配置从各应用中剥离出来,对配置进行统一管理,应用自身不需要自己去管理配置
服务管理提供可视化界面动态管理服务和配置
Nacos 的关键特性包括:
服务发现和服务健康监测
动态配置服务
动态 DNS 服务
服务及其元数据管理
2.Nacos注册中心
管理所有微服务、解决服务之间调用关系错综复杂、难以维护的问题
2.1. 注册中心演变及其设计思想
- 无注册中心:一旦我们这个被调用的服务发生了服务器的一个迁移,或者说发生了服务器的扩容,那么这个时候我们就得手动的去改这个远程服务的地址(调用方源码)
- 手动维护注册中心:查询数据库性能低,扩容服务器,在客户端实现负载均衡困难(微服务架构还没诞生)
- Nginx维护服务列表:服务宕机存在问题,没有对服务实时监控和健康检查功能,成百上千的微服务nginx运维人员进行配置非常复杂,特别针对六一八、双十一进行服务器扩容
- 注册中心:针对Nginx维护复杂问题,出现了注册中心,它是一个服务器,需要进行单独的下载启动,里面有一个注册表用来存储服务列表,提供一系列对外访问的基于http的rest接口,通过这些接口,可以进行动态的注册、获取服务。针对服务集群个别服务器挂掉现象,仍然进行调用,不会自动过滤。
- Nacos注册中心:在基础注册中心的基础上,引入心跳的机制,服务会在本地维护一个定时任务,定时发送心跳到注册中心,在宕机或者调用注销接口将移除服务。 服务调用者还会维护服务列表拉取定时任务,在本地进行缓存,通过客户端负载均衡进行调用。
2.2. 服务注册发现: Nacos Discovery Starter
服务发现是微服务架构体系中最关键的组件之一。如果尝试着用手动的方式来给每一个客户端来配置所有服务提供者的服务列表是一件非常困难的事,而且也不利于 服务的动态扩缩容。Nacos Discovery Starter 可以帮助您将服务自动注册到 Nacos 服务端并且能够动态感知和刷新某个服务实例的服务列表。除此之外,Nacos Discovery Starter 也将服务实例自身的一些元数据信息-例如 host,port,健康检查URL,主页等-注册到 Nacos 。Nacos 的获取和启动方式可以参考 Nacos 官网。
2.3. 核心功能
- 服务注册:Nacos Client会通过发送REST请求的方式向Nacos Server注册自己的服务,提供自身的元数据,比如ip地址、端口等信息。Nacos Server接收到注册请求后,就会把这些元数据信息存储在一个双层的内存Map中。
- 服务心跳:在服务注册后,Nacos Client会维护一个定时心跳来持续通知Nacos Server,说明服务一直处于可用状态,防止被剔除。默认5s发送一次心跳。
- 服务同步:Nacos Server集群之间会互相同步服务实例,用来保证服务信息的一致性。
- 服务发现:服务消费者(Nacos Client)在调用服务提供者的服务时,会发送一个REST请求给Nacos Server,获取上面注册的服务清单,并且缓存在Nacos Client本地,同时会在Nacos Client本地开启一个定时任务定时拉取服务端最新的注册表信息更新到本地缓存
- 服务健康检查:Nacos Server会开启一个定时任务用来检查注册服务实例的健康情况,对于超过15s没有收到客户端心跳的实例会将它的healthy属性置为false(客户端服务发现时不会发现),如果某个实例超过30秒没有收到心跳,直接剔除该实例(被剔除的实例如果恢复发送心跳则会重新注册)
3.Nacos Server部署
下载源码编译
源码下载地址:https://github.com/alibaba/nacos/
可以用迅雷下载
1 cd nacos/
2 mvn ‐Prelease‐nacos clean install ‐U
3 cd nacos/distribution/target/
下载安装包
下载地址:https://github.com/alibaba/Nacos/releases
3.1. 单机模式
3.1.1.服务启动
Windows下载:
Linux下载:
官方文档: https://nacos.io/zhcn/docs/deployment.html
Windos:
解压,进入nacos目录
编辑startup.cmd 修改mode为standalone
运行startup.cmd
Linux:
解压,进入nacos目录
单机启动nacos,执行命令
bin/startup.sh ‐m standalone
也可以修改默认启动方式
访问nocas的管理端:http://192.168.37.1:8848/nacos ,默认的用户名密码是 nocas/nocas
单机模式未修改配置文件数据源默认保存在内存中
3.1.2.服务测试
注册中心架构
Springcloud组件版本对应关系
引入依赖
修改配置文件
启动服务测试效果:
服务调用测试结果:
默认采用ribbon负载均衡
3.2. 集群模式
官网文档: https://nacos.io/zhcn/docs/clustermodequickstart.html
环境应先安装 jdk1.8
第一种,直连模式:
生产环境部署模式:nacos集群部署N台
客户端链接方式:集群ip和端口,都写在配置文件上。随机选择一台nacos节点通信,实现负载均衡;
优点:清晰明白了,没有引入额外的组件。适合中小项目,快速部署和运维
缺点:当nacos部署变更后,client要修改配置。不能动态添加和删除nacos节点。为了解决这个问题,引入了地址服务器,nacos的client去获取nacos集群信息
第二种,vip模式(负载均衡模式)
生产环境部署模式:client ------ vip-------- nacos集群部署N台
引入了VIP组件。VIP组件是阿里的习惯说法。实际可以理解为负载均衡器。第一种模式,客户端要配置集群的所有真实IP。然而集群是变化较快的,而负载均衡器变化就相对小的多,所以client就和真实ip解耦合。
优点:客户端只需要配置连接到VIP组件
缺点:VIP组件依然是ip:port的模式,不容易记忆和理解。一般在内网使用
第三种,域名模式
生产环境部署模式:client---------dns---------vip--------nacos集群
在第二种的基础上,诞生了第三种模式。dns是cloud native应用采用的服务发现方式。dns更加易于理解和记忆
优点:进一步增强了链接配置的可读性,dns也是可以不变,vip挂载在dns下,也可以支持HA,可以应用在互联网上
缺点:多处转发,增加了网络IO时延。引入了多个组件。
3.2.1.服务启动
下载
1 mkdir nacos
2 cd nacos
3 wget https://github.com/alibaba/nacos/releases/download/1.4.1/nacos‐server‐1.4.1.tar.gz
创建多个nacos server
- 单机搭建伪集群,复制nacos安装包,修改为nacos8849,nacos8850,nacos8851
tar ‐zxvf nacos‐server‐1.4.1.tar.gz
mv nacos nacos8849
- 以nacos8849为例,进入nacos8849目录
修改conf\application.properties的配置,使用外置数据源 要使用mysql5.7+(包括)
1 #使用外置mysql数据源
2 spring.datasource.platform=mysql
3
4 ### Count of DB:
5 db.num=1
6
7 ### Connect URL of DB:
8 db.url.0=jdbc:mysql://192.168.8.14:3306/nacos?characterEncoding=utf8&connectTimeout=1000&socketTimeout=3000&autoReconn
ect=true&useUnicode=true&useSSL=false&serverTimezone=UTC
9 db.user.0=root
10 db.password.0=root
3) 将conf\cluster.conf.example改为cluster.conf,添加节点配置
#ip:port
192.168.92.133:8849
192.168.92.133:8850
192.168.92.133:8851
- 创建mysql数据库,sql文件位置:conf\nacosmysql.sql
- 如果出现内存不足:修改启动脚本(bin\startup.sh)的jvm参数
nacos8850,nacos8851 按同样的方式配置。
3.2.2.服务测试
分别启动nacos8849,nacos8850,nacos8851
bin/startup.sh
随便访问一个nacos
客户端配置文件修改
服务调用测试
4.心跳机制
客户端有个定时任务每五秒向注册中心发送心跳,当注册中心15秒还未接收到来自客户端发送的心跳,会将对应服务健康状态设置为false。另一个定时任务会向注册中心拉取服务列表,就不会拉取健康状态为false的服务。当超过30秒还没有收到客户端发送的心跳,注册中心就会把这个服务剔除。
5.Nacos注册中心提供的OPEN API
文档地址:https://nacos.io/zh-cn/docs/open-api.html
6.Nacos配置项介绍
6.1.命名空间(Namespace)
用于进行租户粒度的配置隔离,命名空间不仅适用于nacos的配置管理,同样适用于服务发现。Namespace 的常用场景之一是不同环境的配置的区分隔离,例如开发测试环境和生产环境的资源(如配置、服务)隔离等。
6.2. 分组名称(Group)
对配置和服务进行分组,也是起到隔离作用,当您在 Nacos 上创建一个配置时,如果未填写配置分组的名称,则配置分组的名称默认采用 DEFAULT_GROUP 。配置分组的常见场景:可用于区分不同的项目或应用,例如:学生管理系统的配置集可以定义一个group为:STUDENT_GROUP。
6.3. 服务名
应用注册时候的服务名称
6.4. 保护阈值
进行雪崩保护, 设置0-1之间的值,当服务设置为永久实例(spring.cloud.nacos.discovery.ephemeral =false),服务下线后仍然保存在注册中心,但是健康状态为false,当设置了保护阈值假设为0.6,两个服务实例下线其中一个,当健康实例/总实例=0.5<保护阈值0.6则会进行雪崩保护,下线的服务依然作为可用服务进行调用,分担压力,防止洪峰流量压垮剩下的健康实例。
6.5. 集群(cluster)
服务实例的集合,服务实例组成一个默认集群, 集群可以被进一步按需求划分,比如北京集群、福建集群。Nacos中分为三层隔离,分别是命名空间(namespace)隔离、组(group)隔离、集群(cluster-name)隔离。命名空间是绝对隔离,配置和服务都不可调用。组隔离是同一命名空间下,不同组配置可以共享和继承,但是服务不能跨组调用。而集群隔离,是可以让同一命名空间,同一个分组下,同一集群内服务隔离或不隔离。
集群隔离,主要解决的痛点是:用nacos多人协同开发几个相同的微服务模块时,每个开发人员希望自己本地启动的微服务,会调用到自己本地的另外一个微服务。而公共的微服务还是调用远程的服务器上的微服务。
办法如下:
通过引入如下两个配置:
#指定服务集群
spring.cloud.nacos.discovery. cluster-name=xjf
#添加NacosRule
服务名.ribbon.NFLoadBalancerRuleClassName=com.alibaba.cloud.nacos.ribbon.NacosRule
6.6. 权重
结合负载均衡器权重的机制,设置的值越大,分配的流量越大
7.Nacos AP/CP模型原理
CAP原则
CAP原则又称CAP定理,指的是在一个分布式系统中,一致性(Consistency)、可用性(Availability)、分区容错性(Partition tolerance)这三个要素最多只能同时实现两点,不可能三者兼顾。
C:一致性(Consistency)
一致性要求各个节点查询的数据都一致,如果某个节点由于出现了分区,不能及时同步其他节点的数据,那么这个节点要么停止使用,要么整个系统停止使用。所以如果要保证强一致性,则会牺牲掉节点的可用性!比如zookeeper,当发生网络分区时,为了保证数据一致性,非Leader分区下的节点将变为不可用,并重新进入选举状态。
A:可用性(Availability)
可用性要求所有节点尽量可用,就算出现了网络分区,不同节点之间的数据出现了不一致,但是仍然让该节点可用,所以会牺牲数据一致性。因为从不同节点读取到的数据可能不同!比如eurak、nacos都支持AP架构,当发生网络分区时,所有节点仍然可以读数据,但不保证读到的数据是最新的,不过也不用担心,最终会通过心跳保证数据的最终一致性!
P:分区容错性(Partition tolerance)
CAP协议的P是一定要保证的!不能说发生了网络分区,系统就不能提供服务了
分区:分区是指网络分区。在多节点部署的系统中,由于网络原因,节点之间无法通信、无法同步数据,就出现了网络分区
容错:容错是指当因为网络原因,系统节点出现了分区,对外仍然要提供服务。
7.1. AP/CP选择
在进行服务注册的时候根据ephemeral属性配置服务注册的类型是否为临时节点
可以看到ConsistencyService最终实现类是DelegateConsistencyServiceImpl、PersistentConsistencyService、EphemeralConsistencyService三个类,首先看中间的类命名为委托实现类,作为注入对象注入,但最终的实现还是不是它,它只是一个实现的代理
在调用DelegateConsistencyServiceImpl对象的put方法时,会根据key来判断是否是临时实例,决定最终调用的是哪种实现方式
- 临时实例,选择AP架构,使用Distro协议,分布式协议的一种,阿里内部的协议,服务是放在内存中
- 持久实例,选择CP架构,使用Raft协议来实现,点击查看Raft协议详情!服务是放在磁盘中
注册nacos的client节点注册时ephemeral=true,那么nacos集群对这个client节点的效果就是ap的采用distro,而注册nacos的client节点注册时ephemeral=false,那么nacos集群对这个节点的效果就是cp的采用raft。根据client注册时的属性,ap,cp同时混合存在,只是对不同的client节点效果不同
7.2. AP模式
7.2.1.Distro协议中主要概念
- Memer: 在Nacos-Server启动时,会在cluster.conf中配置整个集群列表,其作用是让每个节点都知道集群中的所有节点,该列表中的每一个节点都抽象成一个Member
- MemberInfoReportTask: 在Nacos-Server启动成功后,会定时给除自己之外的其他Member进行通信,检测其他节点是否还存活。如果通信失败,会将该Member状态置为不健康的,如果后续和该节点重新通信成功,会将该节点的状态置为健康,该Task与Responser的计算密切相关
- Responser: 对于每一个服务来说,在Nacos-Server集群中都会有一个专门的节点来负责。比如集群中有三个健康节点,这三个节点的IP:Port就是组成一个长度为3的List,对三个节点的IP:Port组成的addressList进行排序,这样在每一个节点中,addressList的顺序都是一致的。这时com.ly.OrderService服务注册上来,会根据服务名计算对应的hash值,然后对集群的节点数取余获得下标,从addressList中获取对应的IP:Port,这时这个IP:Port对应的节点就是该服务的Responser,负责该服务的健康检查,数据同步,心跳维持,服务注册。如果客户端服务注册请求到了某个节点,但是本次注册的服务不是由该节点负责,会将该请求重定向到responser的节点去进行处理。注意: 这里的addressList是健康节点,一旦某个节点宕机或者网络发生故障,该节点会从addressList中移除,Service对应的Responser会发生变化。
- HealthChecker: 对于每一个服务,都有一个HealthCheck去对其中的实例进行检测,检测的原则就是该实例最近上报心跳的时间与当前时间的时间差是否超过阈值,如果超过阈值,需要将该实例从服务中摘除。HealthCheck在检测时,也会去进行Responser的检查,只有自己是当前服务的Responser,才会去进行检测。
- DistroTask: 由于Responser规则的存在,对于某一个服务来说,只会有一个node来进行负责,那么其他的node是如何感知到非responser节点的服务数据的呢。DistroTask就是做数据同步的,对当前自己持有的所有服务进行检测,只要有是自己response的,就把该服务的实例数据同步给其他node。这里就有一个优化点,在同步数据时,并不是把该服务下所有的实例全部同步给其他节点,而是对该服务当前所有实例计算一个checksum值(减少传输的数据量,而且一般来说,实例变动是不频繁的)同步到其他节点。其他节点收到数据后,首先会检查同步过来的服务是否是由远端负责,如果是,比对自己节点中该服务的checksum值和远端的是否一致,如果不一致,请求远端节点获取最新的实例数据,再更新本地数据。
- LoadDataTask: 在节点刚启动时,会主动向其他节点拉取一次全量的数据,来让当前节点和整个集群中的数据快速保持一致。
7.2.2.Distro协议的工作流程
- Nacos 启动时首先从其他远程节点同步全部数据。
- Nacos 每个节点是平等的都可以处理写入请求,同时把新数据同步到其他节点。
- 每个节点只负责部分数据,定时发送自己负责数据到其他节点来保持数据一致性。
- 当该节点接收到属于该节点负责的服务时,直接写入。
- 当该节点接收到不属于该节点负责的服务时,将在集群内部路由,转发给对应的节点,从而完成写入。
- 读取操作则不需要路由,因为集群中的各个节点会同步服务状态,每个节点都会有一份最新的服务数据。
- 而当节点发生宕机后,原本该节点负责的一部分服务的写入任务会转移到其他节点,从而保证 Nacos 集群整体的可用性。
如下图,每个order服务可以向任意一个NacosServer发起注册,Nacos服务端集群每个节点都是平等的.
例如我的orderService注册到NacosServer2,然后他先到会到Server2的DistroFilter,通过distroHash(serviceName) % servers.size()哈希当前的服务名取模计算出当前的orderService属于哪个节点,假如计算出属于NacosServer1,则会路由到NacosServer1去注册,NacosServer1注册完之后他会同步到NacosServer2,3,假如这个时候同步失败了,会不断重试,直到成功或者NacosServer2,3节点不存在了就结束.
假设现在出现了分区,AP模式的话不存在主节点的概念,会出现了两个集群现象,这个时候因为客户端可以从任意一个节点拉取数据并且缓存下来,客户端会表现为有时候服务存在有时候服务不存在(假设原先Nacos集群addressList包含 nacos1、nacos2、nacos3的ip,user服务由nacos3负责,order服务由nacos2负责,product服务由nacos1负责,由于网络分区,分成了两个集群,集群1 的addressList包含 nacos1、nacos2,集群2 的addressList包含 nacos3,由于user服务不能通信到nacos3那边,而集群1 addressList包含 nacos1、nacos2,所以user服务Responser发生了变化,变成了nacos1,假如order服务负载均衡访问nacos3节点时,网络不通,重新尝试又负载均衡到nacos1,然后由nacos转发到nacos2完成心跳维持,然后又在集群1内完成同步,nacos1、nacos2互相有了对方负责的服务,product服务也由于通信问题注册到了集群2nacos3上,最终集群1有user、order服务,集群2只有product服务),等网络恢复了,集群之间又开始同步数据达到最终一致.
7.3. CP模式
7.3.1.Raft协议中基本概念
整个raft算法的阶段划分为3个阶段:
- 阶段1:选举阶段。选举出Leader,其他机器为Follower。
- 阶段2:正常阶段。Leader接收写请求,然后复制给其他Follower。
- 阶段3:恢复阶段。旧Leader宕机,新Leader上任,其他Follower切换到新Leader,开始同步数据
Raft 集群必须存在一个主节点(leader),我们作为客户端向集群发起的所有操作都必须经由主节点处理。所以 Raft 核心算法中的第一部分就是选主(Leader election)——没有主节点集群就无法工作,先票选出一个主节点,再考虑其它事情。它会负责接收客户端发过来的操作请求,将操作包装为日志同步给其它节点,在保证大部分节点都同步了本次操作后,就可以安全地给客户端回应响应了。这一部分工作在 Raft 核心算法中叫日志复制(Log replication)。
7.3.2.集群脑裂
形成原因:
是指在多机房(网络分区)部署中,若出现网络连接问题,形成多个分区,则可能出现脑裂问题,会导致数据不一致。
假设我的Nacos1,Nacos2,Nacos3是一个集群,CP模式下,会有一个主节点,假设Nacos1是leader(领导,集群的大脑),他负责写数据以及同步数据到Nacos2,3.现在发生了分区,Nacos3被独立出来了,这个时候Nacos3发现我自己变成一个区域了,这个区域还没有leader,然后把自己选为了leader,这就是脑裂.
这个时候Nacos1和Nacos3都是leader.我们假设这个时候client可以往Nacos1和Nacos3两个集群写数据(Nacos1集群和Nacos3集群网络是不通的),那他一会写1一会写3,就会造成整个集群数据不一致,网络恢复的时候数据要怎么解决冲突呢?
解决方法:
网络恢复的时候已经分不清哪些数据的变化,如果强行合并显然这不是一个很好的方法,所以Nacos(cp)和zookeeper会有一个过半选举机制,当Nacos3想把自己选为leader的时候,需要得到半数以上节点的投票,现在集群3个节点,需要得到2票他才可以选自己为leader,这个时候分区了,显然Naces3是不可能得到2票的,这个时候我们的Nacos3不应该对外提供服务,直到网络恢复,然后Nacos3去Nacos1主节点,把最新的数据给拉下来
在集群启动时,节点个数一般是奇数个,偶数个节点的集群一旦节点对半分区(比如4个节点分区成2个节点和2个节点的情况),整个集群无法选出leader,集群无法提供服务,无法满足CAP中的P,容错能力相同的情况下,奇数节点比偶数节点更节省资源,比如5个节点最多挂掉2个节点还能选leader,6个节点最多也只能挂掉2个节点才能保证可以选leader
CP模式下每个客户端的请求都会被重定向发送给leader,这些请求最后都会被输入到raft算法状态机中去执行。
7.3.3.Raft协议的工作流程
7.3.3.1.Leader选举
raft中,任何一个节点仅有三种状态:
跟随者(Follower):普通群众,默默接收和来自领导者的消息,当领导者心跳信息超时的时候,就主动站出来,推荐自己当候选人Candidate。
候选人(Candidate):候选人将向其他节点请求投票 RPC 消息,通知其他节点来投票,如果赢得了大多数投票选票,就晋升当领导者Leader。
领导者(Leader):一切以我为准。处理写请求、管理日志复制和不断地发送心跳信息,通知其他节点我还存活,不需要发起新的选举。
节点状态转换:
- 初始所有节点都是Follower
- 每个节点都有一个随机睡眠时间,谁先苏醒,谁就从Follower变为Candidate
- Candidata苏醒后向其他Follower发送投票请求,想其他Follower投票给我,让我成为领导者;一旦收到了集群中大多数节点的投票,则从Candidata变为Leader。
- 回到第二步,当我同时有A,B两个节点苏醒为Candidate,但是A获取了大多数选票变为Leader,Candidate B收到Leader心跳之后,变为Follwer
- 假设我有A,B,C三个节点,在Term=1,可以理解为第一年的时候,A是Leader,这个时候由于网络原因分区了,A自己就孤零零了。而这个时候B,C发现Leader不见了,他们从Follwer变为Candidata,重新选举,这个时候Term=2,就是第二年的Leader为B或C其中一个;这个时候A的网络突然又好了,这个集群现在会有两个Leader,这样会有问题,所以当A节点收到更高Term的Leader的心跳之后,A节点从Leader变为Follower,并且更新自己的Term=2。
任期term
自动增加:Follower在等待Leader心跳信息超时后,推荐自己为Candidate,会增加自己的任期号,如节点 A 任期为 0,推举自己为候选人时,任期编号增加为 1,但并不是每个 term 都一定对应一个 leader,有时候某个 term 内会由于选举超时导致选不出 leader,这时 candicate 会递增 term 号并开始新一轮选举。
- 更新为较大值:当节点发现自己的任期编号比其他节点小时,会更新到较大的编号值。比如节点 A 的任期为 1,请求投票,投票消息中包含了节点 A 的任期编号,且编号为 1,节点 B 收到消息后,会将自己的任期编号更新为 1。
- 恢复为跟随者:如果一个候选人或者领导者,发现自己的任期编号比其他节点小,那么它会立即恢复成跟随者状态。这种场景出现在分区错误恢复后,任期为 3 的领导者受到任期编号为 4 的心跳消息,那么前者将立即恢复成跟随者状态。
- 拒绝消息:如果一个节点接收到较小的任期编号值的请求,那么它会直接拒绝这个请求,比如任期编号为 6 的节点 A,收到任期编号为 5 的节点 B 的请求投票 RPC 消息,那么节点 A 会拒绝这个消息。
- 一个任期内,领导者一直都会领导者,直到自身出现问题(如宕机),或者网络问题(延迟),其他节点发起一轮新的选举。
- 在一次选举中,每一个服务器节点最多会对一个任期编号投出一张选票(先到先得),投完了就没了。
选举成功过程:
当candicate从整个集群的大多数(N/2+1)节点获得了针对同一 term 的选票时,它就赢得了这次选举,立刻将自己的身份转变为 leader 并开始向其它节点发送心跳来维持自己的权威。
1、初始状态下,集群中所有节点都是Follower的状态。
2、Raft 算法实现了随机超时时间的特性,每个节点等待领导者节点心跳信息的超时时间间隔是随机的,可以大大减少多个节点同时成为Candidate的概率。比如 A 节点等待超时的时间间隔 150 ms,B 节点 200 ms,C 节点 300 ms。那么 a 先超时,最先因为没有等到领导者的心跳信息,发生超时。
3、当 A 节点的超时时间到了后,A 节点成为候选者,并增加自己的任期编号,Term 值从 0 更新为 1,并给自己投了一票。
Node A:Term = 1, Vote Count = 1。
Node B:Term = 0。
Node C:Term = 0。
4、节点 A 成为候选者后,向其他节点发送请求投票 RPC 信息,请它们选举自己为领导者。
5、节点 B 和 节点 C 接收到节点 A 发送的请求投票信息后,在编号为 1 的这届任期内(同一个任期只能投一票),还没有进行过投票,并且因为自己的term比节点A带过来的term小,把自己的term变为1,就把选票投给节点 A,并增加自己的任期编号。
6、节点 A 收到 3 次投票,得到了大多数节点的投票,从candicate成为本届任期内的新的Leader。(当candicate从整个集群的大多数(N/2+1)节点获得了针对同一 term 的选票时,它就赢得了这次选举,立刻将自己的身份转变为 leader 并开始向其它节点发送心跳来维持自己的权威)
7、节点 A 作为Leader,固定的时间间隔给 节点 B 和节点 C 发送心跳信息,告诉节点 B 和 C,我是Leader,你们不需要重新选举了。
8、节点 B 和节点 C 发送响应信息给节点 A,告诉节点 A 我是正常的。
9、如果B,C节点有一段时间没有收到Leader的心跳,他们又会重复上面步骤,变为Candidate发起投票。
选举超时过程:
如果有多个 follower 同时成为 candidate,选票是可能被瓜分的,如果没有任何一个 candidate 能得到大多数节点的支持,那么每一个 candidate 都会超时。此时 candidate 需要增加自己的 term,然后发起新一轮选举。如果这里不做一些特殊处理,选票可能会一直被瓜分,导致选不出 leader 来。这里的“特殊处理”指的就是前文所述的随机化选举超时时间。
1、A,B,C,D 4个节点都是Follower节点并且term=3,但是这个时候没有Leader,证明term=3的Leader挂掉了或者前几轮选举都没有选出Leader。
2、B和C同时苏醒变为Candidata,这个时候term+1=4,先投自己一票,同时发起投票,节点A投给C,同时更新当前自己的term=4,节点D投给C,这样B,C都是2票,平票,所以一段时间没选举出Leade,选举超时,重新进入休眠状态。
3、超时之后,节点C优先苏醒,这个时候term=5,然后大多数节点投票给节点C,C成为Term5的Leader
4、假设A和D优先收到了B节点的请求,B会成为Leader,先到先得原则,并且已经得到大多数人的投票,并且A和D已经在这个term投过票,则不会再给别人后面继续C又来拉票,也不会投给C。
选举失败:
Candidate 在等待投票回复的时候,可能会突然收到其它自称是 leader 的节点发送的心跳包,如果这个心跳包里携带的 term 不小于 candidate 当前的 term,那么 candidate 会承认这个 leader,并将身份切回 follower。这说明其它节点已经成功赢得了选举,我们只需立刻跟随即可。但如果心跳包中的 term 比自己小,candidate 会拒绝这次请求并保持选举状态。
网络分区term无限增大:
当我们的一个Follower节点网络不通,那么这个时候他没有收到来自Leader的心跳,那么他的term不就一直在增大,当网络好了,这个Follower节点term最大,不就变成Leader了吗?
其实Follower在变为候选者发起投票之前还有一个PerVote阶段,他需要请求其他节点,看正常节点是否多于半数节点,如果多于则自增term发起投票,否则则不会投票以及自增term。
7.3.3.2. 日志复制
共识算法通常基于状态复制机(Replicated State Machine)模型,所有节点从同一个 state 出发,经过一系列同样操作 log 的步骤,最终也必将达到一致的 state。也就是说,只要我们保证集群中所有节点的 log 一致,那么经过一系列应用(apply)后最终得到的状态机也就是一致的。
一旦 leader 被票选出来,它就承担起领导整个集群的责任了,开始接收客户端请求,并将操作包装成日志,并复制到其它节点上去。
整体流程如下:
- Leader 为客户端提供服务,客户端的每个请求都包含一条即将被状态复制机执行的指令,例如client向我们的Leader节点写入数字5。
- Leader 把该指令作为一条新的日志附加到自身的日志集合,然后向其它节点发起附加条目请求(AppendEntries RPC),来要求它们将这条日志附加到各自本地的日志集合。
- 当这条日志已经确保被安全的复制,即大多数(N/2+1)节点都已经复制后,leader 会将该日志 apply 到它本地的状态机中,然后把操作成功的结果返回给客户端。
具体步骤
- 我们客户端往LeaderB写入数据5,这时候数据5是在日志序列中
- LeaderB在下一次心跳把数据5发送给FollowerA和C
- A和C收到之后写入自己的日志序列然后响应给LeaderB
- 收到AC的答复(N/2+1)LeaderB把5提交到状态机
- LeaderB响应给客户端成功
- LeaderB在下一次心跳告诉AC,5已经提交到状态机
- AC把数据5提交到状态机
7.3.3.3.网络分区
网络分区流程:
- LeaderB原本是5个节点的Leader
- 产生分区A,B节点为一个分区,C,D,E节点为另一个分区
- 由于C,D,E发现一定时间收不到Leader心跳,所以自己term+1,然后发起投票
- 最后节点D收到3票成为Term2的Leader
- 假如这个时候有一个客户端往A,B分区的集群写入数据3,他是不会成功的,因为不能复制到大多数节点,得不到大多数节点的响应
- 但是客户端可以往C,D,E分区的集群写入数据8
- 网络好了,A,B收到更高Term的心跳,变为Follower
- A,B收到LeaderD的心跳还发现自己的日志出问题了,丢弃之前未提交的日志,从Leader拉取数据
- 最终整个集群数据一致
8. Nacos配置中心
Nacos 提供用于存储配置和其他元数据的 key/value 存储,为分布式系统中的外部化配置提供服务器端和客户端支持。使用 Spring Cloud Alibaba Nacos Config,您可以在 Nacos Server 集中管理你 Spring Cloud 应用的外部属性配置。
在分布式集群架构,对服务的相同配置(如redis地址)进行修改,存在维护性(频繁更改、需要修改多处,对运维人员挑战性大)、时效性(修改一台重启,另一个可能还没修改)、安全性(配置暴露在项目配置文件中)等问题,nacos可以对配置进行统一的管理,服务能动态感知配置的修改从而进行更新。
8.1. 配置管理界面
8.1.1.配置列表
显示已经创建配置,可以切换查看不同命名空间下的配置
8.1.2.新建配置
Data ID:Data ID 是配置集的唯一标识,一个应用可以包含多个配置集,每个配置集都需要被一个有意义的名称标识。那么 Data ID 怎么取值呢?格式通俗一点就是 “前缀-环境-扩展名”, s p r i n g . c l o u d . n a c o s . c o n f i g . p r e f i x − {spring.cloud.nacos.config.prefix}- spring.cloud.nacos.config.prefix−{spring.profiles.active}.${spring.cloud.nacos.config.file-extension}
- prefix:前缀,默认是 spring.application.name 的值,也可以通过配置项 spring.cloud.nacos.config.prefix 来配置。
- active:配置运行环境,即为当前环境对应的 profile。注意:当 spring.profiles.active 为空时,对应的连接符 ”-“ 也将不存在,dataId 的拼接格式变成 p r e f i x . {prefix}. prefix.{file-extension}
- file-exetension:配置文件的类型,默认是 properties,也可以通过配置项 spring.cloud.nacos.config.file-extension 来配置,目前支持的类型有 TEXT、JSON、XML、YAML、HTML、Properties
Group:在命名空间基础上再进行细粒度的一个归类,比方说我们按照命名空间先给它分为开发环境、生产环境,再按照Group归类各个项目,通常以命名空间进行环境归类,以Group进行项目归类
配置格式:配置文件的格式
配置内容:nacos客户端需要的配置内容
点击发布会在nacos数据库生成对应记录
8.1.3.历史版本
可以查看配置的历史版本和提供回滚操作
8.1.4.监听查询
可以查看当前配置文件有没正确的推送到nacos客户端
8.2. 权限管理
Nacos针对安全性的问题做了权限控制,针对不同的命名空间,可以设置不同的角色读写权限。
-
修改nacos服务application.properties文件:
-
新建用户:
-
新建角色并绑定用户
-
角色授权
-
测试
8.3. Client读取配置
8.3.1.简单测试
-
增加依赖
-
Nacos服务器添加配置
-
新增bootstrap配置
由于8.2设置了权限管理,必须配置用户名密码,服务名与配置的data id相匹配
4. 测试
8.3.2.额外配置
-
如果data Id跟服务名不一致,就要手动指定data id
-
默认情况下nacos客户端读取到的配置文件的文件扩展名是properties,一旦文件格式改变(不是properties)就需要修改nacos客户端配置
-
Profile粒度配置
跟服务名相同的data id的配置文件称之为默认配置文件可以和profile粒度的配置文件一起使用
![在这里插入图片描述](https://img-blog.csdnimg.cn/3145f04701da46a2b47c5d3a0db19863.png#pic_center
-
配置文件优先级
Profile>默认配置文件>extension-configs(针对数组,后读取到的优先级更高)>shared-configs(针对数组,后读取到的优先级更高) -
@RefreshScope
@Value注解可以获取到配置中心的值,但是无法动态感知修改后的值,需要利用@RefreshScope注解