关于分布式:干货一文说透分布式一致性协议下

3次阅读

共计 12295 个字符,预计需要花费 31 分钟才能阅读完成。

本文首发自「慕课网」,想理解更多 IT 干货内容,程序员圈内热闻,欢送关注 ” 慕课网 ”!

作者:大熊老师 | 慕课网讲师


Raft 协定
Paxos 是论证了一致性协定的可行性,然而论证的过程据说艰涩难懂,短少必要的实现细节,而且工程实现难度比拟高广为人知实现只有 zk 的实现 zab 协定。

Paxos 协定的呈现为分布式强一致性提供了很好的实践根底,然而 Paxos 协定了解起来较为艰难,实现比较复杂。

而后斯坦福大学 RamCloud 我的项目中提出了易实现,易了解的分布式一致性复制协定 Raft。Java,C++,Go 等都有其对应的实现。

之后呈现的 Raft 绝对要简洁很多。
引入主节点,通过竞选。
节点类型:Follower、Candidate 和 Leader

Leader 会周期性的发送心跳包给 Follower。每个 Follower 都设置了一个随机的竞选超时工夫,个别为 150ms~300ms,如果在这个工夫内没有收到 Leader 的心跳包,就会变成 Candidate,进入竞选阶段。
根本名词
** 节点状态 **

  • Leader(主节点):承受 client 更新申请,写入本地后,而后同步到其余正本中
  • Follower(从节点):从 Leader 中承受更新申请,而后写入本地日志文件。对客户端提供读申请
  • Candidate(候选节点):如果 follower 在一段时间内未收到 leader 心跳。则判断 leader 可能故障,发动选主提议。节点状态从 Follower 变为 Candidate 状态,直到选主完结

**termId**:任期号,工夫被划分成一个个任期,每次选举后都会产生一个新的 termId,一个任期内只有一个 leader。termId 相当于 paxos 的 proposalId。
**RequestVote**:申请投票,candidate 在选举过程中发动,收到 quorum (多数派)响应后,成为 leader。
**AppendEntries**:附加日志,leader 发送日志和心跳的机制
**election timeout**:选举超时,如果 follower 在一段时间内没有收到任何音讯(追加日志或者心跳),就是选举超时。

两个根本过程

Leader 选举

针对简化版拜占庭将军问题,Raft 解决方案
援用
假如将军中没有叛军,信使的信息牢靠但有可能被暗杀的状况下,将军们如何达成一致性决定?
援用 Raft 的解决方案大略能够了解成 先在所有将军中选出一个大将军,所有的决定由大将军来做。选举环节:比如说当初一共有 3 个将军 A, B, C,每个将军都有一个随机工夫的倒计时器,倒计时一完结,这个将军就会把本人当成大将军候选人,而后派信使去问其余几个将军,能不能选我为总将军?假如当初将军 A 倒计时完结了,他派信使传递选举投票的信息给将军 B 和 C,如果将军 B 和 C 还没把本人当成候选人(倒计时还没有完结),并且没有把选举票投给其余,他们把票投给将军 A,信使在回到将军 A 时,将军 A 晓得本人收到了足够的票数,成为了大将军。在这之后,是否要防御就由大将军决定,而后派信使去告诉另外两个将军,如果在一段时间后还没有收到回复(可能信使被暗杀),那就再重派一个信使,直到收到回复。

失常状况下的 leader 选举

① 5 个节点一开始的状态都是 Follower,termId=1.

② 在一个节点倒计时完结 (Timeout) 后,这个节点的状态变成 Candidate 开始选举,它给其余几个节点发送选举申请 (RequestVote)

③ 四个节点都返回胜利,这个节点的状态由 Candidate
变成了 Leader,并在每个一小段时间后,就给所有的 Follower 发送一个 Heartbeat 以放弃所有节点的状态,Follower 收到 Leader 的 Heartbeat 后重设 Timeout。

这是最简略的选主状况,只有有超过一半的节点投反对票了,Candidate 才会被选举为 Leader,5 个节点的状况下,3 个节点 (包含 Candidate 自身) 投了反对就行。

故障时的 leader 选举

① Leader 出故障挂掉了,其余四个 Follower 将进行从新选主。

② 4 个节点的选主过程和 5 个节点的相似,选出一个新的 Leader

③ 原来的 Leader 复原了又重新加入了,这个时候怎么解决?在 Raft 里,第几轮选举是有记录的,重新加入的 Leader 是第一轮选举 (Term 1) 选出来的,而当初的 Leader 则是 Term 2,所有原来的 Leader 会盲目降级为 Follower。

多个 Candidate 状况下的选主

假如一开始有 4 个节点,都还是 Follower。有两个 Follower 同时 Timeout,都变成了 Candidate 开始选举,别离给另外的 Follower 发送了投票申请。

两个 Follower 别离返回了 ok,这时两个 Candidate 都只有 2 票(含本人一票),要 3 票能力被选成 Leader。两个 Candidate 会别离给另外一个还没有给本人投票的 Follower 发送投票申请。然而因为 Follower 在这一轮选举中,都曾经投完票了,所以都回绝了他们的申请。所以在 Term 2 没有 Leader 被选出来。
这时,两个节点的状态是 Candidate,两个是 Follower,然而他们的倒计时器依然在运行,最先 Timeout 的那个节点会进行发动新一轮 Term 3 的投票。

两个 Follower 在 Term 3 还没投过票,所以返回 OK,这时 Candidate 一共有三票,被选为了 Leader。如果 Leader Heartbeat 的工夫晚于另外一个 Candidate timeout 的工夫,另外一个 Candidate 依然会发送选举申请。

两个 Follower 曾经投完票了,回绝了这个 Candidate 的投票申请。Leader 进行 Heartbeat,Candidate 收到后状态主动转为 Follower,实现选主。
以上是 Raft 最重要流动之一选主的介绍,以及在不同状况下如何进行选主。

复制日志 Log Replication

失常状况下复制日志

Raft 在理论利用场景中的一致性更多的是体现在不同节点之间的数据一致性,客户端发送申请到任何一个节点都能收到统一的返回,当一个节点出故障后,其余节点依然能以已有的数据失常进行。在选主之后的复制日志就是为了达到这个目标。

① 客户端发送申请给 Leader,贮存数据“Hello”,Leader 先将数据写在本地日志,这时候数据还是 Uncommitted (还没最终确认,浅灰示意)

② Leader 给两个 Follower 发送 AppendEntries 申请,数据在 Follower 上没有抵触,则将数据临时写在本地日志,Follower 的数据也还是 Uncommitted。

③ Follower 将数据写到本地后,返回 OK。Leader 收到后胜利返回,只有收到的胜利的返回数量超过半数 (蕴含 Leader),Leader 将数据“sally”的状态改成 Committed。(这个时候 Leader 就能够返回给客户端了)

④ Leader 再次给 Follower 发送 AppendEntries 申请,收到申请后,Follower 将本地日志里 Uncommitted 数据改成 Committed。这样就实现了一整个复制日志的过程,三个节点的数据是统一的。

Network Partition 状况下进行复制日志

在 Network Partition 的状况下,局部节点之间没方法相互通信,Raft 也能保障在这种状况下数据的一致性。

一开始有 5 个节点处于同一网络状态下。Network Partition 将节点分成两边,一边有两个节点,一边三个节点。

两个节点这边曾经有 Leader 了,来自客户端的数据“world”通过 Leader 同步到 Follower。因为只有两个节点,少于 3 个节点,所以“world”的状态仍是 Uncommitted。所以在这里,服务器会返回谬误给客户端。

另外一个 Partition 有三个节点,进行从新选主。客户端数据“Raft”发到新的 Leader,通过和上节网络状态下类似的过程,同步到另外两个 Follower。

因为这个 Partition 有 3 个节点,超过半数,所以数据“Raft”都 Commit 胜利了。

网络状态复原,5 个节点再次处于同一个网络状态下。然而这里呈现了数据抵触“world” 和“Raft”

三个节点的 Leader 播送 AppendEntries。

两个节点 Partition 的 Leader 主动降级为 Follower,因为这个 Partition 的数据“world”没有 Commit,返回给客户端的是谬误,客户端晓得申请没有胜利,所以 Follower 在收到 AppendEntries 申请时,能够把“world“删除,而后同步”Raft”,通过这么一个过程,就实现了在 Network Partition 状况下的复制日志,保障了数据的一致性。

ZAB 协定

概述

Google 的粗粒度锁服务 Chubby 的设计开发者 Burrows 已经说过:“所有一致性协定实质上要么是 Paxos 要么是其变体”。Paxos 尽管解决了分布式系统中,多个节点就某个值达成一致性的通信协议。然而还是引入了其余的问题。因为其每个节点,都能够提议提案,也能够批准提案。当有三个及以上的 proposer 在发送 prepare 申请后,很难有一个 proposer 收到半数以上的回复而一直地执行第一阶段的协定,在这种竞争下,会导致选举速度变慢

所以 zookeeper 在 paxos 的根底上,提出了 ZAB 协定,实质上是,只有一台机器能提议提案(Proposer),而这台机器的名称称之为 Leader 角色。其余参与者表演 Acceptor
角色。为了保障 Leader 的健壮性,引入了 Leader 选举机制。

ZAB 协定还解决了这些问题

1. 在半数以下节点宕机,仍然能对台提供服务
2. 客户端所有的写申请,交由 Leader 来解决。写入胜利后,须要同步给所有的 follower 和 observer
3.leader 宕机,或者集群重启。须要确保曾经再 Leader 提交的事务最终都能被服务器提交,并且确保集群能疾速回复到故障前的状态

ZAB 协定 基本概念

根本名词

  • ** 数据节点(dataNode)**:zk 数据模型中的最小数据单元,数据模型是一棵树,由斜杠(/)宰割的路径名惟一标识,数据节点能够存储数据内容及一系列属性信息,同时还能够挂载子节点,形成一个层次化的命名空间。
  • ** 事务及 zxid**:事务是指可能扭转 Zookeeper 服务器状态的操作,个别包含数据节点的创立与删除、数据节点内容更新和客户端会话创立与生效等操作。对于每个事务申请,zk 都会为其调配一个全局惟一的事务 ID,即 zxid,是一个 64 位的数字,高 32 位示意该事务产生的集群选举周期 epoch(集群每产生一次 leader 选举,值加 1),低 32 位示意该事务在以后抉择周期内的递增秩序(leader 每解决一个事务申请,值加 1,产生一次 leader 抉择,低 32 位要清 0)。
  • ** 事务日志 **:所有事务操作都是须要记录到日志文件中的,可通过 dataLogDir
  • 配置文件目录,文件是以写入的第一条事务 zxid 为后缀,不便后续的定位查找。zk 会采取“磁盘空间预调配”的策略,来防止磁盘 Seek 频率,晋升 zk 服务器对事务申请的影响能力。默认设置下,每次事务日志写入操作都会实时刷入磁盘,也能够设置成非实时(写到内存文件流,定时批量写入磁盘),但那样断电时会带来失落数据的危险。
  • ** 事务快照 **:数据快照是 zk 数据存储中另一个十分外围的运行机制。数据快照用来记录 zk 服务器上某一时刻的全量内存数据内容,并将其写入到指定的磁盘文件中,可通过 dataDir 配置文件目录。可配置参数 snapCount,设置两次快照之间的事务操作个数,zk 节点记录完事务日志时,会统计判断是否须要做数据快照(间隔上次快照,事务操作次数等 SnapCount/2~snapCount 中的某个值时,会触发快照生成操作,随机值是为了防止所有节点同时生成快照,导致集群影响迟缓)。

外围角色

  • **leader**:零碎刚启动时或者 Leader 解体后正处于选举状态;
  • **follower**:Follower 节点所处的状态,Follower 与 Leader 处于数据同步阶段;
  • **observer**:Leader 所处状态,以后集群中有一个 Leader 为主过程。

节点状态

  • **LOOKING**:节点正处于选主状态,不对外提供服务,直至选主完结;
  • **FOLLOWING**:作为零碎的从节点,承受主节点的更新并写入本地日志;
  • **LEADING**:作为零碎主节点,承受客户端更新,写入本地日志并复制到从节点

Zookeeper 集群的模式
Zookeeper 集群的模式包含两种根本的模式: 解体复原 和 音讯播送

解体恢复模式

当整个集群启动过程中,或者当 Leader 服务器呈现网络中弄断、解体退出或重启等异样时,Zab 协定就会 进入解体恢复模式,选举产生新的 Leader。

一但呈现解体,会导致数据不统一,ZAB 的解体复原开始起作用。有如下两个确保:
1.ZAB 协定须要确保曾经在 Leader 提交的事务最终被所有服务器提交。
2.ZAB 协定须要确保抛弃只在 Leader 服务器上被提出的事务。

针对上两个要求,如果 Leader 选举算法保障新选举进去的Leader 服务器领有集群中所有机器最高编号(ZXID 最大)的事务 Proposal,那么就能保障新的 Leader 肯定具备已提交的所有提案,更重要是,如果这么做,能够省去 Leader 服务器查看 Proposal 的提交和抛弃工作的这一步。

一旦 Leader 服务器呈现解体,或者说网络起因导致 Leader 服务器失去了与过半的 Follower 的分割,那么就会进入解体恢复模式。为了保障程序的失常运行,整个复原过程后须要选举一个新的 Leader 服务器。因而,ZAB 协定须要一个高效牢靠的 Leader 选举算法,从而确保可能疾速的选举出新的 Leader。同时,新的 Leader 选举算法不仅仅须要让 Leader 本人晓得其本身曾经被选举为 Leader,同时还须要让集群中所有的其余机器也可能疾速的感知选举产生的新 Leader 服务器。

ZAB 协定规定了如果一个事务 Proposal 在一台机器上被解决胜利,那么应该在所有的机器上都被解决胜利,哪怕机器呈现解体。

音讯播送模式

当新的 Leader 进去了,同时,已有过半机器实现同步之后,ZAB 协定将退出恢复模式。进入音讯播送模式。这时,如果有一台恪守 Zab 协定的服务器退出集群,因为此时集群中曾经存在一个 Leader 服务器在播送音讯,那么该新退出的服务器主动进入恢复模式:找到 Leader 服务器,并且实现数据同步。同步实现后,作为新的 Follower 一起参加到音讯播送流程中。

如果集群中其余机器收到客户端事务申请后,那么会先转发 Leader 服务器,由 Leader 对立解决。

1. 在 zookeeper 集群中,数据正本的传递策略就是采纳音讯播送模式。zookeeper 中数据正本的同步形式与二段提交类似,然而却又不同。二段提交要求协调者必须等到所有的参与者全副反馈 ACK 确认音讯后,再发送 commit 音讯。要求所有的参与者要么全副胜利,要么全副失败。二段提交会产生重大的阻塞问题。

2.Zab 协定中 Leader 期待 Follower 的 ACK 反馈音讯是指“只有半数以上的 Follower 胜利反馈即可,不须要收到全副 Follower 反馈”

3. 整个过程中,Leader 为每个事务申请生产对应的 Proposal,在播送前,为这个事务调配一个全局惟一 ID,为 ZXID(事务 ID),必须依照递增的事务程序进行解决。

ZAB 协定中波及的二阶段提交和 2pc 有所不同。在 ZAB 协定的二阶段提交过程中,移除了中断逻辑,所有 Follower 服务器要么失常反馈 Leader 提出的事务 Proposal,要么就摈弃 Leader 服务器。ZAB 协定中,只有集群中过半的服务器曾经反馈 ACK,就开始提交事务了,不须要期待集群中所有的服务器都反馈响应。这种模型是无奈解决 Leader 服务器解体退出而带来的数据不统一问题的,因而在 ZAB 协定中增加了另一个模式,即采纳解体恢复模式来解决这个问题。此外,整个音讯播送协定是基于具备 FIFO 个性的 TCP 协定来进行网络通信的,因而可能很容易保障音讯播送过程中音讯承受与发送的程序性。

在整个音讯播送过程中,Leader 服务器会为每个事务申请生成对应的 Proposal 来进行播送,并且在播送事务 Proposal 之前,Leader 服务器会首先为这个事务调配一个全局枯燥递增的惟一 ID,咱们称之为事务 ID(即 ZXID)。因为 ZAB 协定须要保障每一个音讯严格的因果关系,因而必须将每一个事务 Proposal 依照其 ZXID 的先后顺序来进行排序与解决。

在音讯播送过程中,Leader 服务器会为每一个 Follower 服务器各自调配一个独自的队列,而后将须要播送的事务 Proposal 顺次放入这些队列中,并且依据 FIFO 策略进行音讯发送。每一个 Follower 服务器在承受到这个事务 Proposal 之后,都会首先将其以事务日志的模式写入到本地磁盘中去,并且在胜利写入后反馈给 Leader 服务器一个 ACK 响应。当 Leader 服务器接管到超过半数 Follower 的 ACK 响应后,就会播送一个 Commit 音讯给所有 Follower 服务器以告诉其将事务进行提交,同时 Leader 本身也会实现事务的提交,而每一个 Follower 服务器收到 Commit 音讯之后,也会实现对事务的提交。

ZAB 的两种基本模式?

解体复原:在失常状况下运行十分良好,一旦 Leader 呈现解体或者因为网络起因导致 Leader 服务器失去了与过半 Follower 的分割,那么就会进入解体恢复模式。为了程序的正确运行,整个复原过程后须要选举出一个新的 Leader, 因而须要一个高效牢靠的选举办法疾速选举出一个 Leader。

音讯播送:相似一个两阶段提交过程,针对客户端的事务申请,Leader 服务器会为其生成对应的事务 Proposal, 并将其发送给集群中的其余所有机器,再别离收集各自的选票,最初进行事务提交。

哪些状况会导致 ZAB 进入恢复模式并选取新的 Leader?

启动过程或 Leader 呈现网络中断、解体退出与重启等异常情况时。

当选举出新的 Leader 后,同时集群中已有过半的机器与该 Leader 服务器实现了状态同步之后,ZAB 就会退出恢复模式。

ZAB 协定 常见的误区

  • ** 写入节点后的数据,立马就能被读到,这是谬误的 **。zk 写入是必须通过 leader 串行的写入,而且只有一半以上的节点写入胜利即可。而任何节点都可提供读取服务。例如:zk,有 1~5 个节点,写入了一个最新的数据,最新数据写入到节点 1~3,会返回胜利。而后读取申请过去要读取最新的节点数据,申请可能被调配到节点 4~5。而此时最新数据还没有同步到节点 4~5。会读取不到最近的数据。如果想要读取到最新的数据,能够在读取前应用 sync 命令。
  • zk 启动节点不能偶数台,这也是谬误的。zk 是须要一半以上节点能力失常工作的。例如创立 4 个节点,半数以上失常节点数是 3。也就是最多只容许一台机器 down 掉。而 3 台节点,半数以上失常节点数是 2,也是最多容许一台机器 down 掉。4 个节点,多了一台机器的老本,然而健壮性和 3 个节点的集群一样。基于老本的思考是不举荐的。

zookeeper 保证数据一致性的协定

而在咱们的 zookeeper 中保证数据一致性用的是 ZAB 协定。通过这个协定来进行 ZooKeeper 集群间的数据同步,保证数据的一致性。

咱们先来简略看一下 zab 协定是工作流程(图):

ZooKeeper 写数据的机制是客户端把写申请发送到 leader 节点上,如果发送的是 follower 节点,follower 节点会把写申请转发到 leader 节点,leader 节点会把数据通过 proposal 申请发送到所有节点,包含本人,所有到节点承受到数据当前都会写到本人到本地磁盘下面,写好了当前会发送一个 ack 申请给 leader,leader 只有承受到过半的节点发送 ack 响应回来,就会发送 commit 音讯给各个节点,各个节点就会把音讯放入到内存中,放内存是为了保障高性能,该音讯就会用户可见了。

那这个时候,若 ZooKeeper 要想保证数据一致性,就需思考如下两个状况

  • 状况一:leader 执行 commit 了,还没来得及给 follower 发送 commit 的时候,leader 宕机了,这个时候如何保障音讯一致性?
  • 状况二:leader 的事务性申请曾经在局部节点利用了,这个时候 leader 挂掉了,如何保障音讯一致性?
  • 如果是 Leader 故障,事务型申请未提交的状况,也就是只在 Leader 服务器上提出但没有提交的操作的操作须要抛弃掉。因为实际上它只是实现了第一阶段。

Leader 故障,事务型申请已提交的状况,也就是已在 Leader 服务器上提交的操作最终被所有的服务器节点都提交。等前面讲到 ZAB 协定,概念就更清晰了,在某个节点提交利用胜利,那这个节点的 zxid 必定更大,决定它会被选举为新的 leader。而后再进行同步。

Leader 选举过程

好,接下来咱们来看一下 Leader 的选举过程,那么对于 Zookeeper 集群来说,当呈现以下两种状况的时候,就须要进行 Leader 选举:

  • 集群启动期间 Leader 选举
  • 集群运行期间 Leader 故障

而后整个 Leader 选举的过程,就是一个投票的过程,而后当某一个节点,它接管到超过半数节点的选票的话,就能够认为它入选 Leader 了。然而这里要投给谁,其实还是有考究的。

ZooKeeper 集群选举外面,当一个节点要决定这个选票要投给谁的时候,会依据两个重要的 ID 来进行判断:

1. 首先是看事务 ID(zxid),哪个服务器上的事务 ID 最大,就投给哪个。【对应这个场景:Leader 故障,事务型申请已提交
2. 那么当两个节点事务 ID 一样的话,就会看节点编号,也就是 myid 外面存的编号,哪个大投给哪个

而在 ZooKeeper 外面,有个轮次的概念,就是每一次投票它其实是有工夫限度的 syncLimit 集群中的 follower 服务器与 leader 服务器之间申请和应答之间能容忍的最多心跳数(tickTime 的数量)。,如果以后投出去的票,没有收到反馈,那么在期待一段时间后,就当作超时解决,这个时候就能够发动一下轮的投票,诶然而有可能,上一轮的反馈它是因为网络提早的问题,这个时候才收到,那么因为当初曾经是开始了下一轮了,所以这个也只能当做有效选票解决了。

选举算法

好 有了这些选举的规范和规定之后,就能够来开始选举了,选举算法:

1. 首先每个节点均发动选举本人为领导者的投票(本人的投给本人);因为刚开始也不晓得哪个节点的事务 ID 最大,所以就先给本人一票再说,接着就给其它节点发消息来拉票,拉票的时候,像在选举的时候一样,拉票的时候就会说本人能力多牛,那么这里也一样,它会把以后节点上最大的事务 ID 给带过来

2. 其它节点收到拉票申请时,就会比拟发起者的事务 ID,是否比本人最新的事务 ID 大,如果比本人的 ID 大,那好就给它投一票吧,否则就不投给它了。那如果事务 ID 相等的话,则比拟发起者的服务器编号。这一点呢咱们后面也说到过,就像选举的时候,先看一下能力谁强,再看一下谁的年纪大。

3. 而后通过每一轮的投票之后,就看有没有哪个节点,它取得的票数(含本人的)大于集群的半数,有的话就阐明这个节点竞选胜利,成为了 Leader 节点,未超过半数且领导者未选出,则再次发动投票。

集群启动期间 Leader 选举

假如一个 Zookeeper 集群中有 5 台服务器,id 从 1 到 5 编号,并且它们都是尚未启动的,没有历史数据。

服务器 1 启动

服务器 1 启动,发动一次选举,服务器 1 投本人一票,此时服务器 1 票数一票,不够半数以上,要大于等于 3 票,选举无奈实现。所以,这个时候对投票后果:服务器 1 为 1 票。

服务器 1 状态放弃为 LOOKING。

服务器 2 启动

服务器 2 启动,也发动一次选举,服务器 1 和 2 别离投本人一票,此时服务器 1 发现服务器 2 的服务 id 比本人大,更改选票投给服务器 2。所以投票后果:服务器 1 为 0 票,服务器 2 为 2 票。

服务器 1,2 状态放弃 LOOKING

服务器 3 启动

服务器 3 也启动了,同时也发动一次选举,服务器 1、2、3 先投本人一票,而后因为服务器 3 的服务器 id 最大,两者更改选票投给为服务器 3;

这时候,投票后果为:服务器 1 为 0 票,服务器 2 为 0 票,服务器 3 为 3 票。此时服务器 3 的票数曾经超过半数,服务器 3 入选 Leader。

服务器 1,2 更改状态为 FOLLOWING,服务器 3 更改状态为 LEADING

服务器 4 启动

服务器 4 启动,也退出进来,发动一次选举,此时服务器 1,2,3 曾经不是 LOOKING 状态,不会更改选票信息。替换选票信息后果:服务器 3 为 3 票,服务器 4 为 1 票。此时服务器 4 遵从少数,更改选票信息为服务器 3。

服务器 4 并更改状态为 FOLLOWING。

服务器 5 启动

与服务器 4 一样投票给 3,此时服务器 3 一共 5 票,服务器 5 为 0 票。

服务器 5 并更改状态为 FOLLOWING。

最终的后果:

服务器 3 是 Leader,状态为 LEADING;其余服务器是 Follower,状态为 FOLLOWING。

好,这就是服务器初始化启动时候的选举过程。

服务器运行期间 Leader 故障

那么,咱们持续来看一下第二种选举过程,服务器运行期间 Leader 故障。

咱们来看一下这个场景:

在 Zookeeper 运行期间 Leader 和 follower 各司其职,当有 follower 服务器宕机或退出不会影响 Leader,然而一旦 Leader 服务器挂了,那么整个 Zookeeper 集群将暂停对外服务,会触发新一轮的选举。

运行期选举

运行期选举与初始状态投票过程根本相似,大抵能够分为以下几个步骤:

  • 状态变更。Leader 故障后,剩下的 follower 节点因为跟 Leader 分割不上了,这个时候它会将本人的服务器状态变更为 LOOKING,而后开始进入 Leader 选举过程。
  • 每个 Server 会收回投票。
  • 接管来自各个服务器的投票,如果其它服务器的数据比本人的新会改投票。
  • 解决和统计投票,每一轮投票完结后都会统计投票,超过半数即可入选。
  • 扭转服务器的状态,发表入选

初始状态下服务器 3 入选为 Leader,假如当初服务器 3 故障宕机了,此时每个服务器上 zxid 可能都不一样,server1 为 10,server2 为 12,server4 为 10,server5 为 10

好,这里咱们以这张图来阐明比拟好容易了解点:

1)第一次投票,每台机器都会将票投给本人。
2)接着每台机器都会将本人的投票发给其它机器,如果发现其它机器的 zxid 比本人大,那么就须要改投票从新投一次。比方 server1

收到了三张票,发现 server2 的 xzid 为 102,pk 一下发现自己输了,前面果决改投票选 server2 为老大,同理,因为从图中很显著看到服务器 2 点 xzxid 是最大的,所以最初是服务器 2 入选 leader 服务器。

简略来将,通常哪台机器服务器上的数据越新,那么越有可能成为 leader,起因也很简略,数据越新,那么它的 zxid 也就越大,也就越可能保证数据的复原。如果集群中有几个雷同的最大的 zxid,那么服务器 id 较大的服务器成为 leader。

同步的过程

在选出 Leader 之后,zk 就进入状态同步的过程。其实就是把最新的 zxid 对应的日志数据,利用到其余的节点中。此 zxid 蕴含 follower 中写入日志然而未提交的 zxid。称之为服务器提议缓存队列 committedLog 中的 zxid。

同步会实现三个 zxid 值的初始化。

**peerLastZxid**:该 learner 服务器最初解决的 zxid。**minCommittedLog**:leader 服务器提议缓存队列 committedLog 中的最小 zxid。**maxCommittedLog**:leader 服务器提议缓存队列 committedLog 中的最大 zxid。零碎会依据 learner 的 **peerLastZxid** 和 leader 的 **minCommittedLog,maxCommittedLog** 做出比拟后做出不同的同步策略

间接差异化同步

场景:**peerLastZxid**介于 **minCommittedLogZxid****maxCommittedLogZxid**

此种场景呈现在,上文提到过的,Leader 收回了同步申请,然而还没有 commit 就 down 了。leader 会发送 Proposal 数据包,以及 commit 指令数据包。新选出的 leader 持续实现上一任 leader 未实现的工作。

例如此刻 Leader 提议的缓存队列为 0x20001,0x20002,0x20003,0x20004,此处 learn 的 peerLastZxid 为 0x20002,Leader 会将 0x20003 和 0x20004 两个提议同步给 learner

先回滚在差异化同步 / 仅回滚同步

此种场景呈现在,上文提到过的,Leader 写入本地事务日志后,还没收回同步申请,就 down 了,而后在同步日志的时候作为 learner 呈现。

例如行将要 down 掉的 leader 节点 1,曾经解决了 0x20001,0x20002,在解决 0x20003 时还没收回提议就 down 了。起初节点 2 入选为新 leader,同步数据的时候,节点 1 又神奇复活。如果新 leader 还没有解决新事务,新 leader 的队为,0x20001, 0x20002,那么仅让节点 1 回滚到 0x20002 节点处,0x20003 日志废除,称之为仅回滚同步。如果新 leader 曾经解决 0x30001 , 0x30002 事务,那么新 leader 此处队列为 0x20001,0x20002,0x30001,0x30002,那么让节点 1 先回滚,到 0x20002 处,再差异化同步 0x30001,0x30002。

全量同步
**peerLastZxid** 小于 **minCommittedLogZxid** 或者 leader 下面没有缓存队列。leader 间接应用 SNAP 命令进行全量同步


欢送关注「慕课网」帐号,咱们会始终保持内容原创,提供 IT 圈优质内容,分享干货常识,大家一起独特成长吧!

本文原创公布于慕课网,转载请注明出处,谢谢合作

正文完
 0