Zookeeper理解---ZAB协议

ZAB协议
  • Zookeeper并不是完全采用Paxos算法,而是使用了一种称为Zookeeper Atomic Broadcast(ZAB,Zookeeper原子消息广播协议)作为数据一致性的核心算法,依据此算法来实现分布式数据一致性的解决。他是一种特别为Zookeeper设计的奔溃可恢复的原子消息广播算法。
  • ZAB协议的核心是定义了对应那些会改变Zookeeper服务器数据状态的师傅请求的处理方式:
    • 所有事务请求必须有一个全局唯一服务器来协调处理,这样的服务器被称为leader服务器
    • 其他服务器成为follower服务器,leader服务器负责讲一个客户端事务请求转换成一个事务Proposal(提议),并将改Proposal分发给集群中所有Follower服务器
    • leader服务器等待所有Follower反馈,等待超过半数Follower进行了正确反馈
    • leader就会再次向所有Follower发出Commons消息,要求将前一个Proposal进行提交
协议介绍
  • ZAB包括两种基本模式:
    • 奔溃恢复: 当整个服务框架启动过程中,或者leader节点出现网络故障或者崩溃退出与重启等异常情况时候,ZAB协议会进入恢复模式进行选举产生新的Leader服务器,当选出来Leader并且同步了半数以上的Follower节点状态(状态同步就是数据同步)后,ZAB才退出恢复模式
    • 消息广播:过半数Follower都与Leader同步完成后,整个服务框架就可以进入消息广播模式了。但一台同样遵循ZAB协议的节点加入后,此时已经有一个Leader节点,新加入的服务会自觉进入恢复模式,同步数据后加入广播流程
    • Leader节点作用在于接受客户端事务请求后,会生成对应的事务提案并发起一轮广播协议,如果集群中其他节点先收到客户端事务请求,他会转发给leader节点
  • 关键字:过半数节点
消息广播
  • ZAB协议的消息广播过程是使用的原子广播协议,类似一个二阶段提交的过程,上面一节提到过,就如下图中所示
    在这里插入图片描述
  • 与二阶段提交不同的是,ZAB的提交过程移除了中断逻辑,也就是不存在应为单个节点没回应ACK而中断广播的情况,所有Follower要么正常反馈,要么抛弃leader服务器,并且只需要过半数的Follower服务器访客ACK后就开始提交事务Proposal,而不需要所有。这种情况有弊端:无法处理leader崩溃带来的数据不一致问题(恢复模式解决此问题),并且整个消息广播协议是有FIFO特性的TCP协议来进行网络通信,能保证消息发送的顺序
顺序性保证
  • 因为只有Leader服务器负责生产对应Proposal来进行广播,并且广播之前,leader会为每一个Proposal分配一个全局唯一递增事务ID(即ZXID)。由此递增ID就可以保证我们每一个Proposal严格按照ZXID顺序来进行处理发送即可
  • 处理好了leader发送顺序,Follower如何保证接受到顺序:广播过程Leader会为每一个Follower分配一个单独队列,依次将Proposal放入队列,并且依据ZXID顺序FIFO策略进行消息发送,每个Follower接受到事务Proposal会以事务日志形式写入本地磁盘,成功写入后反馈给Leader服务器一个Ack响应。
  • Leader收到超半数ACK后,广播一个Commit消息给所有Follower服务器以通知进行事务提交,同事Leader自己也完成Commit。
奔溃恢复
  • Leader节点奔溃后需要触发选举,选出新Leader,并让所有节点能够快速的感知到新的Leader服务器是哪个
基本特性
  • 崩溃恢复可能出现两种数据不一致的隐患, 针对这两种情况ZAB需要保证的特性:

  • 情况一: ZAB协议需要确保已经在Leader服务器上提交的服务最终被所有服务器都提交

  • 事务在Leader服务器上被提交了,并且已经得到过半Follower服务器的ACK反馈,但在Commit消息发送给所有Follower机器之前Leader服务器挂了,如下图:
    在这里插入图片描述

  • 上图leader是Server1,已经广播消息P1,P2,C1,P3和C2,当leader将C2(C2 是Commit of proposal2的缩写)发出后崩溃,此时ZAB协议需要保证Proposal2最终能在所有服务器上被提交

  • 情况二:ZAB需要确保丢弃那些只在Leader服务器上被提出的事务。

  • 与之前相反的情况,奔溃恢复过程中出现一个需要被丢弃的案例,那么恢复后需要跳过次事务:
    在这里插入图片描述

  • 上图Leader服务器Server1 提出一个事务Proposal3后就奔溃,其他follower都没有收到这个事务,当Server1恢复再次加入集群,ZAB需要确保丢弃这个Proposal3

Leader选举
  • 根据以上两种情况确保提交已经被Leader提交的事务Proposal同时丢弃被跳过的事情,需要ZAB必须有这样的Leader选举算法:
    • 保证选举出来的Leader服务器拥有集群中所有机器最高编号(最大ZXID)事务Proposal,那么可以把这这个新选举出来的Leader一定具有所有已经提交的案例,并且可以省去服务器检查proposal的提交和丢弃工作的这部操作,所有事务按最高ZXID节点拥有的数据为准。
数据同步
  • Leader选举后,Leader服务器为每一个Follower服务器准备一个队列,将那些没有被Follower服务器同步的事务以Proposal消息的方法逐个发给Follower服务器,并且在每个Proposal消息后紧接着发一个Commit消息,标识已经提交。等同步完成所有消息并且将数据应用到本地数据库中后,leader会将Follower服务器加入到真正的可用Follower列表,并且开始之后的流程。
丢弃数据
  • 如何处理需要被丢弃的Proposal,ZAB协议变化ZXID设计上解决这个问题:
  • ZXID是一个64位的数据,低32位是一个单调递增的计数器,针对每个事物请求计数器+1操作,高位32位代表Leader周期epoch的编号,每单选举产生一个新的Leader服务器,就会从Leader服务器上去除其本地日志中最大事物Proposal的ZXID,
    并从该ZXID中解析出上一次的epoch值,然后在这个基础上+1,就用此编号作为新的epoch,就相当于每一个Leader都递增1 的一个策略。ZAB协议通过epoch编号来区分Leader周期变化策略,能有效避免不同Leader服务器错误使用相同ZXID变化踢出不一样事务Proposal的异常情况,对于识别崩溃后在恢复之后生成的Proposal非常有帮助。
结论
  • 基于上面的策略,当一个包含上一个Leader周期中没提交的事务Proposal的服务器复活的时候,肯定无法成为Leader,因为当前集群中包含一个Quorum集合,该集合中一定包含了更高epoch的事务Proposal,因此当前复活的节点事务Proposal肯定是旧的,无法成为Leader,当加入集群后,只能以Follow角色链接Leader服务器,接着Leader会根据自己服务器上最后被提交的Proposal来和Follower的Proposal对比,接着会按当前Leader的匹配要求同步当前Leader的数据(也就相当于回退)到一个确实已经被集群中过半集群提交的最新事务Proposal。
深入ZAB协议
  • 上面部分介绍类ZAb协议大体内容以及实际运行中消息官博和奔溃恢复这两个基本模式。以下从系统模型,问题描述,算法描述,运行分析深入理解ZAB协议
系统模型
  • 分布式系统模型中我门将每个server看作是一个进程p,那么可以用集合∏={P1,P2,…,Pn}来表示分布式系统,每个进程都具有格子的存储设备,就好比缓存隔离,个进程直接互相通信来实现消息传递。每个进程都肯奔溃或重启,奔溃我们称为down踢出集合∏,恢复存活状态我们称为up重新加入∏,只要∏ 集合中有超过半数的是up状态就可以进行消息广播,我们将这样一个自集合称为Quorum(上文提到过)之后用Q表示,并假设Q已经存在满足以下:
//存在任意Q Q是 ∏ 的子集
∧ ∀Q,Q ⊆ ∏
//存在任意时候的Q1 Q2,他们的交集不等于空集合(此处的意义在于Q1或Q2 任意一个都超过半数,应此不管如何去交集都能得到一个子集合,极端情况是正好半数多一个元素的情况)
∧ ∀Q1和Q2, Q1∩Q2≠∅
完整性
  • 进程P_j如果收到来自进程P_i的消息m,那么p_i一定发送来消息m
前置性
  • 进程P_j收到来消息m,那么存在这样的消息m’:如果消息m’ 是消息m的前置,那么P_j务必先收到m’,这种关系表示:m’ < m
问题描述
  • Zookeeper需要解决一系列诸如可靠配置存储,运行时状态记录等分布式协调服务,因此必须具备高吞吐量,低延迟特性,能很好的在高并发下完成分布式数据一致性处理,同时能优雅处理运行时故障,快速恢复
  • ZAB核心是只有一个主进程,如果主进程挂了就重选,主进程选举与消息广播密切相关。随着时间推移可能出现无限多个主进程并构成一个主进程序列:P_1,P_2,P_3…P_e-1,P_e其中P_e ⊆ ∏ ,e表示主进程序列好吗,我们这里称他为主进程周期。
  • 对于以上序列中任意一个如果存在e < e’,那么我们就说P_e 是P_e’ 之前的主进程,使用 P_e < P_e’ ,他门是同一个进程只不过是不同周期而已
主进程周期
  • 用以上 P_e < P_e’ 的案例来解释,就比如进程P_e周期中主节点是L_e,此时主节点L_e 挂掉了,重新选出L_e’(e’=e+1)就得到了P_e’,其实是同一个进程只是表示他在不同的Leader周期下而已,我们表示为e < e’
事物
  • 几个概念,我们假设各个进程中有一个transactions(v,z)这样的函数,用来实现主进程堆状态变更的广播,其中参数v:事物内容,z:事物表示,而每一个事物表示z=<e,c>,e表示进程周期,c表示周期内事物技术,也就是我们上文中提到的epoch(z)表示事物标识中的主进程周期epoch,counter(z)表示事务计数器
  • 每个新事务c都递增,实际运行中事务z 优与事务 z’ ,有两种情况:
    • epoch(z) < epoch(z’) :z是上一个主进程周期中的事务,z’是重新选举后的事务
    • epoch(z) = epoch(z’) 且 counter(z) < counter(z’) 同一个主进程周期,但是z’事务序列号更大
算法描述
  • ZAB协议注意包括消息广播和崩溃恢复,进一步可以分三个阶段:发现(Discovery),同步(Synchronization),广播(Broadcast),先定义如下专用标识和属于
术语名说明
F.pFollower f处理过的最后一个事务Proposal
F.zxidFollower f处理过的历史事务Proposal中最后一个Proposal的事务表示ZXID
h_f每一个Follower f通常都已经处理(接受)了不少事务Proposal,并且会有一个针对已经处理过的事务的集合,将其表示为Hf,表示Follower f已经处理过的事务的序列
I_e初始化历史记录,在某个主进程周期epoch中,当准Leader完成阶段一之后,此时他的h_f就被标记为I_e
阶段一:发现
  • 主要是Leader选举过程,用于分布式进程中选举主进程,准Leader L,和Follower F工作流程:(姑且认为Leader L是随机出来的)

    • 步骤F.1.1 Follower F将自己最后接受的事务Proposal的epoch值CEPOCH(F.p)发送给准Leader L
    • 步骤L.1.1 当接受来自过半的Follower的Cepoch(F.p)消息后,准Leader L会生成NEWEPOCH(e’),这个epoch的值e’ 是Leader L从CEPOCH(F.p)消息中选取的最大epoch,然后对其+1得到e’(就是获取最大一个ZXID的值从中得到epoch,然后在+1)
    • 步骤F1.2 当Follower 接受到来自准Leader L的NEWEPOCH(e’)消息后,如果其检测到当前CEPOCH(F.p)小于e’,那么就将CEPOCH(F.p)=e’,同时向这个准Leader L反馈Ack消息。这个反馈消息(ACK-E(F.p, h_f)),包含当前该Follower的epoch(F.p)也就是最后一个事务的信息,以及该Follower的历史事务Proposal列表:h_f
  • 当Leader L接受到来自过半Follower的确认消息Ack后,Leader L就会从这过半服务器中选取出一个Follower F,使用他的I_e最为初始化事务结婚I_e’

  • 关于这个Follower F的选取对于Quorum中其他任意一个Follower F’ ,F需要满足以下两个条件中的一个:

//任意Follower F'的epoch主节点周期值小于 Follower F主节点周期值(主节点周期值也就是Leader节点序列号每次选举+1这个)
CEPOCH(F'.p) < CEPOCH(F.p)
//如果两节点Follower F与Follower F'的epoch值相同也就是在同一个主节点周期,那么我们比较Follower F'处理的最后一个事务的ZXID要小于 选出来的Follower F处理的最后一个事务的ZXID,也就是我们选出来的必然是up集合中拥有最大ZXID事务的Follower节点,或者两个都相同则都可以是Leader列表的备选
(CEPOCH(F'.p) = CEPOCH(F.p)) & ( F'.zxid < F.zxid 或 F'.zxid = F.zxid)
阶段二:同步
  • 发现流程后,进入同步节点,此时Leader L与Follower F流程如下:
    • L.2.1 Leader 会将e’和I_e’以NEWLEADER(e’, I_e’)消息的形式发送给Quorum中的Follower(将新的epoch主节点序列号和 最全的事务列表同步)
    • 步骤F.2.1 当Follower接受到来自Leader L的NEWLEADER(e’, I_e’)消息后,如果Follower发现CEPOCH(F.p)≠ e’,那么直接跳过进入下一轮循环因为此时Follower 发现自己还在上一轮,或者更上一个的同步中,因为需要在发现阶段就已经将epoch修改为e’,他可能还在发现阶段属于另外一半还没收到Leader L的NEWEPOCH(e’)消息的节点
    • 如果CEPOCH(F.p) = e’ ,那么Follower会执行事务应用操作,具体的对每一个事务Proposal:<v, z > ⊆ I_e’, Follower都会接受<e’, <v, z>>。最后Follower会反馈给Leader,表示字节已经接受并处理来所有I_e’中的事务,(此处的意思是Leader L节点将最新的一份Proposal列表的信息以及最新的epoch数据发送给每一个Follower节点让他去同步修改,如果Follower中的Proposal是在其中,那么我就将之前Proposal里面的epoch值也就是e修改为新的epoch值也就是e’)
    • 步骤L.2.2 当Leader 接受到来自过半Follower 针对NEWWLEADER(e’, I_e’)的反馈消息后,就会向所有Follower发送消息Commit,至此Leader 完成阶段二
    • 步骤F.2.2 当Follower收到来自Leader的Commit后,就会一次处理并提交所有在I_e’中为未理的事务,至此Follower完成阶段二
阶段三:广播
  • 完成同步后ZAB开始正式工作,接受客户端新事务请求,进行广播流程
  • 步骤L.3.1 Leader L接受到客户端新的事务请求,会生成对应的事务Proposal,并且根据ZXID顺序向Follower发送提案<e’, <v, z>>,其中epoch(z) = e’,z就是ZXID,64位,低32 是Proposal事务序列号,高32位是Leader周期的epoch序列号
  • 步骤F.3.1 Follower根据消息接收先后顺序处理来自Leader事务的Proposal,并将他们追加到h_f中去,之后在反馈给Leader
  • 步骤L.3.1 当Leader接收到来自过半Follower针对事务Proposal<e’, <v, z>>的Ack消息后,就发送Commit<e’, <v, z>>消息给所有Follower,要求他们进行事务的提交
  • 步骤F.3.2 当Follow F接受到来自Leader的Commit<e’, <v, z>> 就会开始提交Proposal<e’, <v, z>>,需要主要,此时该Follower F必定已经提交来事务Proposal<v’, z’ >,其中<v’, z’ > ⊆ h_f, z’ < z
总结

如下图是以上三个流程总结,各个消息说明如下:
在这里插入图片描述

  • 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
  • PROPOSE:Leader进程生成一个针对客户端事务请求的Proposal
  • ACK:Follower进程反馈Leader进程发来的PROPOSAL消息
  • COMMIT:Leader发送COMMIT消息,要求所有进程提交事务PROPOSE

上一篇:Zookeeper–简介
下一篇:Zookeeper–ZAB与Paxos算法联系与区别

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值