我们知道分布式系统是由多个节点组成的,那么这些节点是怎么进行协调并且有条不絮工作的呢?其实,这些节点当中是有一个老大(master主节点)的,都是靠这个老大进行管理的。分布式主节点就是保证整个系统有序的进行各种业务操作,所以我们今天的话题就是分布式系统中如何确定主节点。
01
为什么需要分布式选主
现在我们已经知道了什么是分布式主节点了,现在我们假如节点中没有主节点,那么这样每个节点都可以接收写请求,此时的系统数据就会出现不一致的情况,试想下,ES或者MySql集群,如果多个节点写的话,是不是数据全乱套了,所以这就是为什么我们在设计分布式系统时候需要进行选主操作,总结一句话,分布式选主是为了让它来管理和协调其他节点,以保证整个集群有序运行以及节点间的数据一致性。
02
怎么进行分布式选主
我们已经知道分布式选主是什么也知道了为什么要选主了,那现在我们想一下,怎么才能让集群中的节点选出那个老大呢?这里就需要我们根据业务以及各种硬件等环境进行设计合适选主算法,目前业界内常用算法有基于序号选主的算法(Bully算法)、多数派选主算法(Raft算法和ZAB算法)等。下面我们来看看这几种算法
Bully算法--霸道算法
Bully之所以说是霸道算法,是因为它遵循的是“长者”为大的原则,即集群中节点Id值最大的那个则会被选为主节点。
Bully算法在选主的过程中,会涉及到下面三种消息:
# | 消息类型 | 消息描叙 |
---|---|---|
1 | Election消息 | 用于发起选举 |
2 | Alive消息 | 对Election消息的应答 |
3 | Victory | 竞选成功的节点向其他节点宣誓主权 |
那么Bully算法的底层选主流程是什么样子的呢?
触发选主的时机有两个:
1,错误节点恢复
2,检测到master节点发生故障,如上图
结合上图,总结下Bully算法选举流程:
- 集群中所有节点都会判断自己的Id是否最大的,如果是最大的,则会像其他节点直接发送Victory,进行宣誓主权。
- 如果自己并不是当前存活节点最大的,则需要向比自己大的节点发送Election消息,并等待其他节点的回复。
- 在规定的时间范围内,若没有收到任何节点的Alive消息,则会认为自己成为主节点,并像其他节点发送Victory,宣誓自己是老大了;若收到了比自己大的节点回复的Alive消息,则自己会等待Victory的通知
- 若收到了比自己小的节点发来的Election消息,则会回复个Alive消息过去,告诉它我比大。
Bully算法霸道且简单,优点是选举速度快、算法复杂度低、简单易实现。
其缺点是,所有节点必须得知道每个节点的相关信息(如上图例中id值),需要额外的信息存储;另一个则是,若一个ID值比现在集群中master大的节点新加入或者故障恢复,则会触发新一轮选主,如果这台节点不稳定频繁退出,则会出现当前分布式系统频繁的进行切主。
Raft算法--投票
Raft算法是属于典型的多数派选主算法,就像我们实际生活中的投票选举一样,大到常委会选举,小到普通民主选举,其核心思想就是少数派服从多数派,谁获得的票数最多,谁就可以成为主节点。
在正式讲解Raft算法实现之前,先了解下Raft算法选主中集群各个节点的角色,一共有3中角色:
Leader: 为主节点,同一时刻只有一个Leader节点,负责整个集群的节点间的协调和管
理。
Candidate: 候选节点,只有角色为候选者的节点才可以被选为新的Leader,每个节点 都有可以成为候选者。
Follower: Leader的跟随者,这个角色的时候不可以发起选主。
Raft算法选举流程:
- 初始化时,各个节点均为Follower状态。
- 开始选主时,所有节点的Follower状态转为Candidate状态,并向其他节点发送选主请求。
- 其他节点根据收到的选主请求的先后顺序,进行回复是否同意其成为主节点;每个节点只能投一张票。
- 发起选主的节点如果得到一半以上的投票,则会成为主节点,状态变为Leader,其他几点则会由Candidate转为Follower状态,此时Leader和Follower将保持心跳检测。
- 如果Leader节点的任期到了,Leader则会降为Follower,进行新一轮选主。
节点间状态转换关系如下图:
触发选主时机,Leader出现故障,Leader任期满(就像投票选主成功的节点有一定的任期时间)
Raft总结:Raft 算法具有选举速度快、算法复杂度低、易于实现的优点;其缺点是,集群中各个节点均需要相互通信,且需要半数以上的节点同意才能成为主,因此通信量比较大;为何说Raft的稳定性要比Bully算法好呢,是因为它在新节点加入或者故障节点恢复的时候,并不一定像Bully那样可能频繁切主,除非投票数过半才会真的切主。
ZAB算法--优先级的民主投票
ZAB(ZooKeeper Atomic Broadcast)算法是ZooKeeper为实现分布式协调而设计的,它是对Raft选主算法的一种改进,在拥有投票选举的维度下增加节点ID以及数据ID(数据的最新值)为参考。
ZAB选主算法,节点同样也有3种角色:
Leader:主节点。
Follower:跟随者
Observe:观察者,观望,无投票权
ZAB选举过程中,集群节点用下如下4个状态:
# | 状态名 | 状态描叙 |
---|---|---|
1 | Looking | 选举状态,此状态下,集群中没有Leader,自己进入选主状态 |
2 | Leading | 领导者状态,已经选好了主,当前节点为Leader |
3 | Following | 跟随者状态,选完Leader后,其他节点转为Following |
4 | Observing | 观察者状态,持观望态度 |
在讲ZAB具体算法之前,我们先要了解下,ZAB算法使用的节点元数据信息,每个节点都有一个唯一的三元组(server_id,server_zxId,epoch);server_id即为当前节点的Id,server_zxId即为当前节点存放数据的最新值id,epoch即为当前选主进行的轮数。
ZAB算法在选主过程在投票给具体节点时,通过(vote_id,vote_zxId)表示投票给哪个节点,vote_id即为被投票的节点id,vote_zxid为被秃瓢节点的zxid;其ZAB选主算法的唯一原则是,server_zxId值最大则成为Leader,如果相同,则看server_id最大者。
ZAB选主算法选主流程:
1.系统初始化时,即为第一轮投票选主,即epoch=1,所有的节点zxID=0,此时所有节点都会推选自己,且将选票信息(epoch,vote_id,vote_zxid)广播出去,如下图
2.此时epoch和zxId都是相同,根据ZAB算法判断原则,则会依据server_id最大者为推选对象,所以节点会更新vote_id=3进行重新广播
3.现在所有的节点都推线了Node3节点,则node3成为Leader,为Leading状态,其他节点为Following状态,Leader与Follower将会建立心跳机制。
ZAB总结,ZAB选主算法性能较高,对系统没有特殊的要求,采用的是广播的模式发送偷跑信息,如果集群中节点比较多,每个节点同时广播,则会有n*(n-1)个消息,容易发生广播风暴,同时还增加节点Id和数据Id参考,则ZAB在选主时间上相对长一点,那为何还是性能最优的,是因为它不会容易频繁出现切主的情况。
最后,我们基于Bully、Raft以及ZAB算法做个总结:
先依据个算法的消息回复类型、选主机制、过程以及性能方面做个对比
再来看下使用情况的思维导图
下一集预告:会分享下高并发项目中的三座大山,高性能、高可用以及可扩展