ZAB

本文主要是以下两篇文章的整合:
Zab
ZAB 协议

算法简介

Zab是Zookeeper Atomic Broadcast协议的简称,是为分布式协调服务Zookeeper专门设计的一种支持崩溃恢复的原子广播协议。因为 Zookeeper 是一种主备结构( primary-backup scheme ),所以 Zab 协议具备将主机的状态更新按顺序的广播到备份机的功能,并且能够保证在主机崩溃后不会出现主备数据不一致情况。
正常情况下,在ZooKeeper中所有的事务请求都由一个主服务器也就是Leader来处理,其他服务器为Follower,Leader将客户端的事务请求转换为事务Proposal并为其分配事务id,并且将Proposal分发给集群中其他所有的Follower,然后Leader等待Follwer反馈,当有过半数(>=N/2+1)的Follower反馈信息后,Leader将再次向集群内Follower广播Commit信息,Commit为将之前的Proposal提交。

算法流程

ZAB协议主要包括消息广播和崩溃恢复两个过程,进一步可以细分为发现(discovery)、同步(sync)和广播(Broadcast)阶段。而每个 Zab 节点在任意某个时刻只会处于三个阶段中的某一个阶段,同时我们又称这三个阶段组成了一次 Zab 的迭代,在任何时候 Zab 节点都可能放弃当前的迭代,通过重新进入 Phase 1 开始新的迭代。在发现之前还有选主阶段,并没有在协议里体现,Zookeeper实现了FastLeaderElection算法。


image.png

在经过 Phase 1 后只能说该 Zab 节点根据 Leader Oracle 被选择成为了 Leader,但是此时它还没有完全建立起领导关系(LeaderShip),也就是它目前还不具备领导其它的 Zab 节点的能力,只有当它完成了 Phase 2 的同步后才真正建立了对其它 Zab 节点的领导关系,

步骤

  1. 当一个 Zab 节点发现 Leader Election 算法输出的 Leader 是它的时候,它会立即进入到 Phase 1 开启新的迭代;
  2. 在开始新的迭代后它首先会收集超过法定数量 Follower 中最新的 Epoch,然后将其递增后作为 NEWEPOCH 提议发送给刚刚超过法定数量的 Follower ,并从它们返回的 ACK 信息中再次收集最新的 Epoch 和最大的 zxid ;
  3. 最终当 Leader 从 Follower 返回的 ACK-E(f.a,hf) 信息中获取到具备最新 Epoch 和最大 zxid 的 Follower 的 hf 后它就完成了 Phase 1 ,也就是完成了新 Epoch 的确立;
  4. 进入到 Phase 2 后该 Leader 会通过发送 NEWLEADER(e,Ie) 提议的方式来提议它自己成为真正的 Leader ,其中 Ie 就是最新 Epoch e 的初始化数据;
  5. 当 Leader 一旦接收到超过法定数量的 Follower 对 NEWLEADER 的 ACK 确认信息就可以被确立为真正的 Leader 了,同时它会发送 COMMIT 提议给 Follower 让它们提交同步后的新提议;
  6. 而当 Follower 接收到确立后的新 Leader 的 COMMIT 提议后就会提交它们之前同步过但未提交的所有新提议,至此完成 Phase 2;

节点状态

Looking:系统刚启动时、leader与过半follower失去心跳时、Leader崩溃后其他follower切换的状态
Following:Follower节点所处的状态,Follower与Leader保持同步状态
Leading:Leader所处状态

关键交互报文

CEPOCH:Follower进程向准Leader发送自己处理过的最后一个事务Proposal的epoch值
NEWEPOCH:准Leader进程根据接收的各进程的epoch,来生成一个新一轮周期的epoch值
ACK-E:Follower进程反馈准Leader进程发来的NEWEPOCH消息
NEWLEADER:准Leader进程确定自己的领导地位,并发送NEWLEADER消息给各进程
ACK-LD:Follower进程反馈Leader进程发来的NEWLEADER消息
COMMIT-LD:要求Follower进程提交相应的历史Proposal
PROPOSAL:Leader进程生成一个针对客户端事务请求的Proposal
ACK:Follower进程反馈Leader进程发来的PROPOSAL消息
COMMIT:Leader发送COMMIT消息,要求所有进程提交事务PROPOSE

算法分析

事务编号 Zxid :Zxid 是一个 64 位的数字,其中低 32 位是一个简单的单调递增的计数器,针对客户端每一个事务请求,计数器加 1;而高 32 位则代表 Leader 周期 epoch 的编号,每个当选产生一个新的 Leader 服务器,就会从这个 Leader 服务器上取出其本地日志中最大事务的ZXID,并从中读取 epoch 值,然后加 1,以此作为新的 epoch。
epoch:类似Raft中的term,标明leader的版本,每次leader变更都会进行提升,这样可以用于fence之前老的leader崩溃恢复之后重新加入。

Phase 0: Leader election

节点在一开始都处于选举阶段,只要有一个节点得到超半数节点的票数,它就可以当选准 leader。只有到达 Phase 3 准 leader 才会成为真正的 leader。这一阶段的目的是就是为了选出一个准 leader,然后进入下一个阶段。协议并没有规定详细的选举算法。

Phase 1: Discovery

伪代码

zab的实现不是基于RPC的,而是面向socket的方式。选举开始后节点会建立与其他节点的连接,并发送自己的epoch和lastZxid,其他节点回复currentEpoch(cepoch)。准Leader收集Follower的cepoch,并根据max(cepoch)+1生成newEpoch,再通知给其他节点更新其acceptEpoch。newEpoch通知过程中,Follower会反馈其历史事务proposal集合。这样准Leader就可以判断出当前集群中多数节点中哪个节点拥有最新的数据。


image.png

其中,
history:当前节点接收到事务提议的 log
acceptedEpoch:follower 已经接受的 leader 更改epoch的 NEWEPOCH 提议
currentEpoch:当前所处的epoch
lastZxid:history 中最近接收到的提议的 zxid (最大的)

详细步骤

整个算法过程,关键的持久化数据有,
epoch :纪元,一个纪元相当于一个时代,一个时代仅存在唯一的一个 Leader 领导者;
f.p :Follower 最后确认的更新 Epoch 提议 NEWEPOCH(e’) 的纪元 e’(Last new epoch proposal follower f acknowledged);
f.a :Follower 最后确认的更新 Leader 提议 NEWLEADER(e’,T) 的纪元 e’(Last new leader proposal follower f acknowledged);
hf :Follower 的历史数据记录;
f.zxid :在 hf 中最后接受的事务标识符;

image.png
  1. 首先 Follower f 给未来预期的 Leader l 通过发送 CEPOCH(f.p) 来传递其确认过的最后一个更新 Epoch 提议 NEWEPOCH(e’) 的纪元信息 f.p;
  2. Leader l 一旦接收到达到法定数量的 Follower 发送的 CEPOCH(e) ,则立即发送 NEWEPOCH(e’) 给刚刚发送过 CEPOCH(e) 的 Follower,其中这里的 e’ 记为比刚刚接收到的所有 CEPOCH(e) 中的 e 都大的纪元(e’ = emax+1),并且此处开始将达到法定数量时发送过 CEPOCH(e) 的 Followers 记为集合 Q ;
  3. Follower 接收到 Leader l 发送的 NEWEPOCH(e’) 提议即开始进行判断,如果 f.p < e’ 则意味着新纪元 e’ 大于其最后接收到的更新纪元提议中的纪元,因此更新 f.p 为 e’ 表示其接收到的最后一个更新纪元提议的纪元为 e’ ,同时发送 ACK-E(f.a ,hf) 给 Leader l 确认 NEWEPOCH(e’) 提议,其中 f.a 表示其最后确认过的更新 Leader 提议 NEWLEADER(e’,T) 的纪元信息,而 hf 代表其的历史数据记录,到这里 Follower 就完成了 Phase 1 ;
  4. Leader l 一旦接收到达到法定数量的 Follower 对 NEWEPOCH(e’) 的确认,则立即从所有回复的 ACK-E(f.a ,hf) 中挑选合适的 hf 作为初始化数据 Ie’(这里也可以理解为从集合 Q 的 Follower 中挑选包含合适 hf 的 Follower ),具体的挑选规则为:对于 f1 和 f2 两个 Follower 回复的 ACK-E(f1.a ,hf1) 和 ACK-E(f2.a ,hf2) ,如果 f1.a < f2.a 则选择 hf2 ;若存在 f1.a == f2.a ,则当 f1.zxid <= f2.zxid 选择 hf2,总结一下也就是首先判断 ACK 中 f.a 的值,f.a 值大者获选,若两者的 f.a 相等则继续判断 f.zxid 的值,f.zxid 值大者获选,到这里 Leader l 就完成了 Phase 1;

Phase 2: Synchronization

伪代码

同步阶段主要是利用 leader 前一阶段获得的最新提议历史,同步集群中所有的副本。准Leader先与Discovery阶段发现的各个Follower节点的历史proposal集合,判断出最新数据节点,然后与其进行同步。
当准Leader同步完成最新数据之后,再通过NewLeader向其他节点通知其当选为新的Leader,并向其同步数据。只有当 quorum 都同步完成,准 leader 才会成为真正的 leader。follower 只会接收 zxid 比自己的 lastZxid 大的提议。


image.png

详细步骤

image.png
  1. 首先未来预期的 Leader l 会给 Phase 1 中 Q 集合里所有的 Follower 发送更新 Leader 的提议 NEWLEADER(e’,Ie’) ;
  2. Follower 接收到 Leader l 发送的 NEWLEADER(e’, Ie’) 提议后开始进行判断,如果 f.p ≠ e’ 则立即回到 Phase 1 的第一步开始新的轮回,如果 f.p == e’ 则开始执行下面的原子操作:首先更新 f.a 为 e’ ,然后接收 Ie’ 中所有纪元为 e’ 的数据,将自身的 hf 设置为 Ie’ ,完成上述原子操作最后回复确认 NEWLEADER(e’, Ie’) 提议的信息给 Leader l ;
  3. Leader l 一旦接收到达到法定数量 Follower 对 NEWLEADER(e’, Ie’) 提议的确认就会给所有回复的 Follower(也即 Phase 1 的 Q 集合中所有的 Follower )发送 commit 提议,到这里 Leader l 完成 Phase 2 ;
  4. 当 Follower 接收到 Leader l 发送的 commit 提议后会按照 Ie’ 中的顺序对接收到的 Ie’ 中的事务依次进行提交,到这里 Follower 完成 Phase 2 ;

Phase 3: Broadcast

伪代码

到了这个阶段, leader才能对外提供服务,可以进行消息广播。数据同步的过程类似一个2PC,Leader将client发过来的请求生成一个事务proposal,然后发送给Follower,多数Follower应答之后,Leader再发送Commit给全部的Follower让其进行提交。与2PC相比,只需要多数同意即可,而且也移除了中断逻辑。


image.png

image.png

详细步骤

image.png
  1. Leader l 按顺序递增 zxid 后给 Phase 2 中所有已确认关系的 Follower(也即 Phase 1 的 Q 集合中所有的 Follower )发送纪元为 e’ 的提议 (e’,<v,z>),这里因为在 Phase 2 中已经根据 Ie’ 完成了前面所有 e’ 纪元提议的 commit ,所以可以认为到目前为止对于纪元为 e’ 的提议的 zxid 都是顺序递增的;
  2. 首先如果 Follower 起初正处于 Leading 状态,则调用 ready(e’) ,否则 Follower 将按照 Leader l 发送的顺序接收提议,并将其添加到 hf 的尾部,最后回复 ACK 确认给 Leader l ;
  3. 当 Leader l 接收到达到法定数量 Follower 回复的对于提议 (e’,<v,z>) 的 ACK 确认信息后,就会向它们发送 COMMIT(e’,<v,z>) 提议指示 Follower 提交它们刚刚接收的提议 (e’,<v,z>) ;
  4. 当 Follower 接收到 Leader l 发送的 COMMIT(e’,<v,z>) 提议后就会通过调用 abdeliver(<v,z>) 方法来提交包括 (e’,<v,z>) 提议在内的和与其同一纪元且排在其前面的所有 (e’,<v’,z’>) 提议,其中存在 <v’,z’> ∈ hf 且 z’ < z;
  5. 同时在整个 Phase 3 中如果 Leader l 接收到来自 Follower 的 CEPOCH(e) 信息就会一次性的回复 NEWEPOCH(e’) 和 NEWLEADER(e’, Ie’ ◦ βe’) 两个提议来同步该 Follower ;
  6. 如果 Leader l 在发送完 NEWEPOCH(e’) 和 NEWLEADER(e’, Ie’ ◦ βe’) 两个提议后接收到该 Follower 对 NEWLEADER(e’, Ie’ ◦ βe’) 的 ACK 确认信息,就会直接发送 commit 提议让该节点提交可同步的事务,从而让该 Follower 完成 Phase 2 进入到 Phase 3,同时 Leader l 也会将该节点添加到自己的发送目标集合 Q 中;
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,530评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 86,403评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,120评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,770评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,758评论 5 367
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,649评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,021评论 3 398
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,675评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,931评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,659评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,751评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,410评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,004评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,969评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,203评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,042评论 2 350
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,493评论 2 343

推荐阅读更多精彩内容