深入分析zookeeper实现原理之ZAB协议(2)

上一篇博文主要阐述了zookeeper的集群安装。接下来将结合相关资料与书籍,讲解自己对zookeeper的认识与理解,也算自己对zookeeper的知识整理吧,有不对的地方希望能指正。

zookeeper设计思想与由来

zookeeper 主要是解决分布式环境下的服务协调问题而产 生的,如果我们要去实现一个 zookeeper 这样的中间件, 我们需要做什么?

  1. 防止单点故障 如果要防止zookeeper这个中间件的单点故障,那就势 必要做集群。而且这个集群如果要满足高性能要求的话, 还得是一个高性能高可用的集群。高性能意味着这个集 群能够分担客户端的请求流量,高可用意味着集群中的 某一个节点宕机以后,不影响整个集群的数据和继续提 供服务的可能性。
  • 结论: 所以这个中间件需要考虑到集群,而且这个集群 还需要分摊客户端的请求流量
  1. 接着上面那个结论再来思考,如果要满足这样的一个高 性能集群,我们最直观的想法应该是,每个节点都能接 收到请求,并且每个节点的数据都必须要保持一致。要 实现各个节点的数据一致性,就势必要一个leader节点 负责协调和数据同步操作。这个我想大家都知道,如果 在这样一个集群中没有leader节点,每个节点都可以接
    收所有请求,那么这个集群的数据同步的复杂度是非常 大。
  • 结论:所以这个集群中涉及到数据同步以及会存在 leader节点
  1. 继续思考,如何在这些节点中选举出leader节点,以及 leader挂了以后,如何恢复呢?
  • 结论:所以zookeeper用了基于paxos理论所衍生出来 的ZAB协议
  1. leader 节点如何和其他节点保证数据一致性,并且要求 是强一致的。在分布式系统中,每一个机器节点虽然都 能够明确知道自己进行的事务操作过程是成功和失败, 但是却无法直接获取其他分布式节点的操作结果。
  • 结论:当一个事务操作涉及到跨节点的时候,就需要用到分布 式事务,分布式事务的数据一致性协议有 2PC 协议和 3PC协议。

基于这些猜想,我们基本上知道zookeeper为什么要用到 zab 理论来做选举、为什么要做集群、为什么要用到分布 式事务来实现数据一致性了。接下来我们逐步去剖析 zookeeper里面的这些内容

zookeeper 的集群

在zookeeper中,客户端会随机连接到zookeeper集群中 的一个节点,如果是读请求,就直接从当前节点中读取数 据,如果是写请求,那么请求会被转发给leader提交事务, 然后 leader 会广播事务,只要有超过半数节点写入成功, 那么写请求就会被提交(类2PC事务)
zookeeper集群交互
所有事务请求必须由一个全局唯一的服务器来协调处理,这个服务器就是 Leader 服务器,其他的服务器就是follower。leader服务器把客户端的失去请求转化成一个事务 Proposal(提议),并把这个 Proposal 分发给集群中的所有 Follower 服务器。之后 Leader 服务器需要等待所Follower服务器的反馈,一旦超过半数的Follower服务器进行了正确的反馈,那么 Leader 就会再次向所有的Follower服务器发送Commit消息,要求各个follower节点对前面的一个Proposal进行提交;

集群角色

  • Leader 角色
    Leader服务器是整个zookeeper集群的核心,主要的工作任务有两项,即:事物请求的唯一调度和处理者,保证集群事物处理的顺序性;集群内部各服务器的调度者 。
  • Follower 角色
    Follower角色的主要职责是:1. 处理客户端非事物请求、转发事物请求给leader服务器 2. 参与事物请求 Proposal 的投票(需要半数以上服务器 通过才能通知leader commit数据; Leader发起的提案, 要求Follower投票) 3. 参与Leader选举的投票
  • Observer 角色
    Observer 是 zookeeper3.3 开始引入的一个全新的服务器 角色,从字面来理解,该角色充当了观察者的角色。 观察zookeeper集群中的最新状态变化并将这些状态变化 同步到 observer 服务器上。Observer 的工作原理与 follower 角色基本一致,而它和 follower 角色唯一的不同 在于 observer 不参与任何形式的投票,包括事物请求 Proposal的投票和leader选举的投票。简单来说,observer 服务器只提供非事物请求服务,通常在于不影响集群事物 处理能力的前提下提升集群非事物处理的能力

集群组成

通常zookeeper是由2n+1台server组成,每个server都 知道彼此的存在。对于2n+1台server,只要有n+1台(大 多数)server可用,整个系统保持可用。我们已经了解到, 一个zookeeper集群如果要对外提供可用的服务,那么集 群中必须要有过半的机器正常工作并且彼此之间能够正常 通信,基于这个特性,如果向搭建一个能够允许 F 台机器 down 掉的集群,那么就要部署 2*F+1 台服务器构成的 zookeeper 集群。因此 3 台机器构成的 zookeeper 集群, 能够在挂掉一台机器后依然正常工作。一个 5 台机器集群 的服务,能够对 2 台机器怪调的情况下进行容灾。如果一台由6台服务构成的集群,同样只能挂掉2台机器。因此,5 台和 6 台在容灾能力上并没有明显优势,反而增加了网 络通信负担。系统启动时,集群中的server会选举出一台 server为Leader,其它的就作为follower(这里先不考虑 observer角色)。 之所以要满足这样一个等式,是因为一个节点要成为集群 中的 leader,需要有超过及群众过半数的节点支持,这个 涉及到leader选举算法。同时也涉及到事务请求的提交投票。

ZAB 协议

ZAB(Zookeeper Atomic Broadcast) 协议是为分布式协 调服务ZooKeeper 专门设计的一种支持崩溃恢复的原子 广播协议。在 ZooKeeper 中,主要依赖 ZAB 协议来实现 分布式数据一致性,基于该协议,ZooKeeper 实现了一种 主备模式的系统架构来保持集群中各个副本之间的数据一 致性。

  • ZAB 协议介绍

ZAB协议包含两种基本模式,分别是:1. 崩溃恢复 2. 消息广播

当整个集群在启动时,或者当 leader 节点出现网络中断、 崩溃等情况时,ZAB 协议就会进入恢复模式并选举产生新 的Leader,当 leader服务器选举出来后,并且集群中有过 半的机器和该leader节点完成数据同步后(同步指的是数 据同步,用来保证集群中过半的机器能够和leader服务器 的数据状态保持一致),ZAB协议就会退出恢复模式。 当集群中已经有过半的 Follower 节点完成了和 Leader 状 态同步以后,那么整个集群就进入了消息广播模式。这个 时候,在Leader节点正常工作时,启动一台新的服务器加 入到集群,那这个服务器会直接进入数据恢复模式,和 leader 节点进行数据同步。同步完成后即可正常对外提供 非事务请求的处理。

  • 消息广播的实现原理
    如果大家了解分布式事务的2pc和3pc协议的话,消息广播的过程实际上是一个 简化版本的二阶段提交过程
  1. leader 接收到消息请求后,将消息赋予一个全局唯一的 64位自增id,叫:zxid,通过zxid的大小比较既可以实 现因果有序这个特征
  2. leader为每个follower准备了一个FIFO队列(通过TCP 协议来实现,以实现了全局有序这一个特点)将带有zxid的消息作为一个提案(proposal)分发给所有的follower
  3. 当follower接收到proposal,先把proposal写到磁盘, 写入成功以后再向leader回复一个ack
  4. 当leader接收到合法数量(超过半数节点)的ACK后, leader就会向这些follower发送commit命令,同时会 在本地执行该消息
  5. 当 follower 收到消息的 commit命令以后,会提交该消息
    在这里插入图片描述
    leader 的投票过程,不需要 Observer 的 ack,也就是 Observer不需要参与投票过程,但是Observer必须要同 步 Leader 的数据从而在处理请求的时候保证数据的一致 性

崩溃恢复(数据恢复)

ZAB 协议的这个基于原子广播协议的消息广播过程,在正 常情况下是没有任何问题的,但是一旦 Leader 节点崩溃, 或者由于网络问题导致 Leader 服务器失去了过半的Follower节点的联系(leader失去与过半follower节点联 系,可能是leader节点和follower节点之间产生了网络分 区,那么此时的leader不再是合法的leader了),那么就 会进入到崩溃恢复模式。在ZAB协议中,为了保证程序的 正确运行,整个恢复过程结束后需要选举出一个新的 Leader 为了使 leader 挂了后系统能正常工作,需要解决以下两 个问题:

  1. 已经被处理的消息不能丢失
    当 leader 收到合法数量 follower 的 ACKs 后,就向 各个 follower 广播 COMMIT 命令,同时也会在本地 执行 COMMIT 并向连接的客户端返回「成功」。但是如 果在各个 follower 在收到 COMMIT 命令前 leader 就挂了,导致剩下的服务器并没有执行都这条消息。
    在这里插入图片描述
  2. 被丢弃的消息不能再次出现
    当 leader 接收到消息请求生成 proposal 后就挂了,其
    他 follower 并没有收到此 proposal,因此经过恢复模式
    重新选了 leader 后,这条消息是被跳过的。 此时,之前
    挂了的 leader 重新启动并注册成了 follower,他保留了
    被跳过消息的 proposal 状态,与整个系统的状态是不一
    致的,需要将其删除。

ZAB 协议需要满足上面两种情况,就必须要设计一个 leader 选举算法:能够确保已经被 leader 提交的事务 Proposal能够提交、同时丢弃已经被跳过的事务Proposal。 针对这个要求 1. 如果 leader 选举算法能够保证新选举出来的 Leader 服 务器拥有集群中所有机器最高编号(ZXID最大)的事务 Proposal,那么就可以保证这个新选举出来的Leader一 定具有已经提交的提案。因为所有提案被 COMMIT 之 前必须有超过半数的 follower ACK,即必须有超过半数 节点的服务器的事务日志上有该提案的 proposal,因此,
只要有合法数量的节点正常工作,就必然有一个节点保 存了所有被 COMMIT 消息的 proposal 状态 另外一个,zxid是64位,高32位是epoch编号,每经过 一次 Leader 选举产生一个新的 leader,新的 leader 会将 epoch 号+1,低 32 位是消息计数器,每接收到一条消息 这个值+1,新 leader选举后这个值重置为0.这样设计的好 处在于老的leader挂了以后重启,它不会被选举为leader, 因此此时它的 zxid 肯定小于当前新的 leader。当老的 leader 作为 follower 接入新的 leader 后,新的 leader 会 让它将所有的拥有旧的 epoch 号的未被 COMMIT 的 proposal 清除。

  • 关于 ZXID
    zxid,也就是事务id, 为了保证事务的顺序一致性,zookeeper 采用了递增的事 务 id 号(zxid)来标识事务。所有的提议(proposal)都 在被提出的时候加上了 zxid。实现中 zxid 是一个 64 位的 数字,它高32位是epoch(ZAB协议通过epoch编号来 区分 Leader 周期变化的策略)用来标识 leader 关系是否 改变,每次一个 leader 被选出来,它都会有一个新的 epoch=(原来的epoch+1),标识当前属于那个leader的 统治时期。低32位用于递增计数。
    epoch可以理解为当前集群所处的年代或者周期,每个 leader 就像皇帝,都有自己的年号,所以每次改朝换代, leader 变更之后,都会在前一个年代的基础上加 1 。这样 就算旧的 leader 崩 溃 恢 复 之 后 ,也 没 有 人 听 他 的 了 ,因 为 follower 只听从当前年代的 leader 的命令。

leader 选举

Leader选举会分两个过程:启动的时候的leader选举、 leader崩溃的时候的的选举

  • 启动的时候的leader选举

服务器启动时的 leader 选举 每个节点启动的时候状态都是LOOKING,处于观望状态, 接下来就开始进行选主流程 进行Leader选举,至少需要两台机器(具体原因前面已经 讲过了) ,我们选取3台机器组成的服务器集群为例。在集 群初始化阶段,当有一台服务器Server1启动时,它本身是 无法进行和完成Leader选举,当第二台服务器Server2启 动时,这个时候两台机器可以相互通信,每台机器都试图 找到Leader,于是进入Leader选举过程。选举过程如下:

  1. 每个Server发出一个投票。由于是初始情况,Server1 和 Server2 都会将自己作为 Leader 服务器来进行投 票,每次投票会包含所推举的服务器的myid和ZXID、 epoch,使用(myid, ZXID,epoch)来表示,此时Server1 的投票为(1, 0),Server2的投票为(2, 0),然后各自将 这个投票发给集群中其他机器。
  2. 接受来自各个服务器的投票。集群的每个服务器收到 投票后,首先判断该投票的有效性,如检查是否是本 轮投票 (epoch)、是否来自LOOKING状态的服务器。
  3. 处理投票。针对每一个投票,服务器都需要将别人的 投票和自己的投票进行PK,PK规则如下:
  • i. 优先检查 ZXID。ZXID 比较大的服务器优先作为 Leader 。
  • ii. 如果ZXID相同,那么就比较myid。myid较大的 服务器作为Leader服务器。 对于 Server1 而言,它的投票是(1, 0),接收 Server2 的投票为(2, 0),首先会比较两者的ZXID,均为0,再 比较myid,此时Server2的myid最大,于是更新自 己的投票为(2, 0),然后重新投票,对于Server2而言, 它不需要更新自己的投票,只是再次向集群中所有机 器发出上一次投票信息即可
  1. 统计投票。每次投票后,服务器都会统计投票信息, 判断是否已经有过半机器接受到相同的投票信息,对 于Server1、Server2而言,都统计出集群中已经有两 台机器接受了(2, 0)的投票信息,此时便认为已经选出 了Leader。
  2. 改变服务器状态。一旦确定了Leader,每个服务器就 会更新自己的状态,如果是Follower,那么就变更为 FOLLOWING,如果是Leader,就变更为LEADING。
  • 运行过程中的 leader 选举

当集群中的 leader 服务器出现宕机或者不可用的情况时, 那么整个集群将无法对外提供服务,而是进入新一轮的 Leader 选举,服务器运行期间的 Leader 选举和启动时期 的Leader选举基本过程是一致的。

  1. 变更状态。Leader挂后,余下的非Observer服务器 都会将自己的服务器状态变更为 LOOKING,然后开 始进入Leader选举过程。
  2. 每个Server会发出一个投票。在运行期间,每个服务 器上的ZXID可能不同,此时假定Server1的ZXID为 123,Server3的ZXID为122;在第一轮投票中,Server1 和Server3都会投自己,产生投票(1, 123),(3, 122), 然后各自将投票发送给集群中所有机器。接收来自各 个服务器的投票。与启动时过程相同。
  3. 处理投票。与启动时过程相同,此时,Server1将会成 为Leader。
  4. 统计投票。与启动时过程相同。
  5. 改变服务器的状态。与启动时过程相同

下一篇再来详细分析zookeeper核心之:watcher机制。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值