Zookeeper 概述及ZAB协议

一、Zookeeper 简介

    ZooKeeper 是一个开源的分布式应用程序协调服务器,其为分布式系统提供一致性服务。 其一致性是通过基于 Paxos 算法的 ZAB 协议完成的。其主要功能包括:配置中心、注册中心、 分布式同步、集群管理等。

1.功能简介

(1)配置中心

分布式系统中,很多服务都是部署在集群中的,即多台服务器中部署着完全相同的应用, 起着完全相同的作用。当然,集群中的这些服务器的配置文件是完全相同的。

若集群中服务器的配置文件需要进行修改,那么我们就需要逐台修改这些服务器中的配 置文件。如果我们集群服务器比较少,那么这些修改还不是太麻烦,但如果集群服务器特别 多,比如某些大型互联网公司的 Hadoop 集群有数千台服务器,那么纯手工的更改这些配置 文件几乎就是一件不可能完成的任务。即使使用大量人力进行修改可行,但过多的人员参与, 出错的概率大大提升,对于集群的形成的危险是很大的。

这时候 Zookeeper 就可以派上用场了。其对于配置文件的维护采用的是“发布/订阅模型”。发布者将修改好的集群的配置文件发布到 Zookeeper 服务器的文件系统中,那么订阅者马上就可以接收到通知,并主动去同步 Zookeeper 里的配置文件。Zookeeper 具有同步操作的原子性,确保每个集群服务器的配置文件都能被正确的更新。

(2) 注册中心

在分布式应用中,一个项目包含多个工程,而这些工程中,有些工程是专门为其它工程 提供服务的。一个项目中可能会存在多种提供不同服务的工程,而一种服务又可能存在多个 提供者(服务器)。所以,用于消费这些服务的客户端工程若要消费这些服务,就变的异常的复杂了。

此时,Zookeeper 就可以上场了。为每个服务起个名称,将这些服务的名称与提供这些 服务的主机地址注册到 Zookeeper 中,形成一个服务映射表。服务消费者只需要通过服务名 称即可享受到服务,而无需了解服务具体的提供者是谁。服务的减少、添加、变更,只需修改 Zookeeper 中的服务映射表即可。

但实际上 Zookeeper不适合做分布式注册中心,参考https://blog.csdn.net/chaoyuehu/article/details/90513674

(3)分布式同步

在分布式系统中,很多运算(对请求的处理)过程是由分布式机群中的若干服务器共同计算完成的,并且它们之间的运算还具有逻辑上的先后顺序。如何保证这些服务器运行期间的同步性呢?

使用 Zookeeper 可以协调这些服务器间运算的过程。让这些服务器都同时监听 Zookeeper 上的同一个 znode(Zookeeper 文件系统中的一个数据存储节点),一旦其中一个服务器 Update 了 znode,那么另一个相应服务器能够收到通知,并作出相应处理。

(4)集群管理

集群管理中最麻烦的就是节点故障管理。Zookeeper 可以让集群选出一个健康的节点作 为 Master,Master 随时监控着当前集群中的每个节点的健康状况,一旦某个节点发生故障, Master 会把这个情况立即通知给集群中的其它节点,使其它节点对于任务的分配做出相应 调整。Zookeeper 不仅可以发现故障,也会对故障进行甄别,如果该故障可以修复,Zookeeper 可以自动修复,若不能修复则会告诉系统管理员错误的原因让管理员迅速定位问题。

Hbase Master选举则是zookeeper经典的使用场景;

2.一致性要求

什么是 zk 的一致性呢?其需要满足以下几点要求:

(1) 顺序一致性

从同一个客户端发起的 n 多个事务请求(写请求),最终将会严格按照其发起顺序被应用到 ZooKeeper 中。

(2) 原子性

所有事务请求的结果在集群中所有机器上的应用情况是一致的。也就是说要么整个集群 所有主机都成功应用了某一个事务,要么都没有应用,不会出现集群中部分主机应用了该事 务,而另外一部分没有应用的情况。

(3) 单一视图
无论客户端连接的是哪个 ZooKeeper 服务器,其看到的服务端数据模型都是一致的。

(4) 可靠性

一旦服务端成功地应用了一个事务(写操作成功),并完成了对客户端的响应,那么该事务所引起的服务端状态变更将会被一直保留下来,除非有另一个事务又对其进行了变更。

(5) 实时性

通常人们看到实时性 的第一反应是,一旦一个事务 被成功应用,那 么客户端能够 立即从 服务端上读取到这个事务变更后的最新数据状态。这里需要注意的是,ZooKeeper 仅仅保证在一定的时间段内,客户端最终一定能够从服务端上读取到最新的数据状态。

二、ZAB协议

2.1 ZAB 协议简介

ZAB,Zookeeper Atomic Broadcast,zk 原子消息广播协议,是专为 ZooKeeper 设计的一种支持崩溃恢复的原子广播协议,是一种 Pasox 协议的优化算法。在 Zookeeper 中,主要依赖 ZAB 协议来实现分布式数据一致性

Zookeeper 使用一个单一主进程来接收并处理客户端的所有事务请求,即写请求。当服 务器数据的状态发生变更后,集群采用 ZAB 原子广播协议,以事务提案 Proposal 的形式广 播到所有的副本进程上。ZAB 协议能够保证一个全局的变更序列,即可以为每一个事务分配 一个全局的递增编号 xid。

当Zookeeper 客户端连接到 Zookeeper 集群的一个节点后,若客户端提交的是读请求, 那么当前节点就直接根据自己保存的数据对其进行响应;如果是写请求且当前节点不是 Leader,那么节点就会将该写请求转发给 Leader,Leader 会以提案的方式广播该写操作,只要有超过半数节点同意该写操作,则该写操作请求就会被提交。然后 Leader 会再次广播给所有订阅者,即 Learner,通知它们同步数据。

2.2 Zookeeper集群角色

为了避免 Zookeeper 的单点问题,zk 也是以集群的形式出现的。zk 集群中的角色主要有以下三类:

 (1)Leader:zk 集群写请求的唯一处理者,并负责进行投票的发起和决议,更新系统状态。 Leader 是很民主的,并不是说其在接收到写请求后马上就修改其中保存的数据,而是首先根据写请求提出一个提议,在大多数 zkServer 均同意时才会做出修改。

 (2)Follower:接收客户端请求,处理读请求,并向客户端返回结果;将写请求转给Leader; 在选主(选 Leader)过程中参与投票。

  (3)Observer:可以理解为无选主投票权与写操作投票权的 Flollower,其不属于法定人数范围,主要是为了协助 Follower 处理更多的读请求。如果 Zookeeper 集群的读请求负载很高时,势必要增加处理读请求的服务器数量。若增加的这些服务器都是以 Follower 的身份出现,则会大大降低写操作的效率。因为 Leader 发出的所有写操作提议,均需要通 过法定人数半数以上同意。过多的 Follower 会增加 Leader 与 Follower 的通信压力,降低写操作效率。同样,过多的 Follower 会延长 Leader 的选举时长,降低整个集群的可 用性。此时,可选择增加 Observer 服务器,既提高了处理读操作的吞吐量,又没有增加法定人数。只要法定人数不变,无论是写操作投票还是选主投票,其都不会增加通信压力,都不会影响投票效率。

2.3 三种模式

ZAB 协议中对 zkServer 的状态描述有三种模式:恢复模式、同步模式和广播模式。
 (1)恢复模式:在服务重启过程中,或在 Leader 崩溃后,就进入了恢复模式,要恢复到 zk集群正常的工作状态。

(2)同步模式:在所有的 zkServer 启动完毕,或 Leader 崩溃后又被选举出来时,就进入了 同步模式,各个 Follower 需要马上将 Leader 中的数据同步到自己的主机中。当大多数 zkServer 完成了与 Leader 的状态同步以后,恢复模式就结束了。所以,同步模式包含在 恢复模式过程中。

(3)广播模式:当 Leader 的提议被大多数 zkServer 同意后,Leader 会修改自身数据,然后会将修改后的数据广播给其它 Follower。

2.4 zxid

zxid 为 64 位长度的 Long 类型,其中高 32 位表示纪元 epoch,低 32 位表示事务标识 xid。 即 zxid 由两部分构成:epoch 与 xid。

每个 Leader 都会具有一个不同的 epoch 值,表示一个时期、时代。每一次新的选举开启时都会生成一个新的 epoch,新的 Leader 产生,则会更新所有 zkServer 的 zxid 中的 epoch。 xid 则为 zk 的事务 id,每一个写操作都是一个事务,都会有一个 xid。xid 为一个依次递增的流水号。每一个写操作都需要由 Leader 发起一个提案,由所有 Follower 表决是否同意本次写操作,而每个提案都具有一个 zxid。

2.5 消息广播算法

当集群中已经有过半的 Follower 与 Leader 服务器完成了状态同步,那么整个 zk 集群就 可以进入消息广播模式了。

如果集群中的其他节点收到客户端的事务请求,那么这些非 Leader 服务器会首先将这 个事务请求转发给 Leader 服务器。Leader 服务器会为其生成对应的事务提案 Proposal,并将 其发送给集群中其余所有的主机,然后再分别收集它们的选票,在选票过半后进行事务提交。 其具体过程如下:

Leader 接收到消息请求后,将消息赋予一个全局唯一的 64 位自增 id,即 zxid,通过 zxid 的大小比较即可实现事务的有序性管理。

为了保证 Leader 向 Follower 发送提案的有序,Leader 会为每个 Follower 创建一个 FIFO 队列,并将提案副本写入到各个队列。然后再通过这些队列将提案发送给各个 Follower。

当 Follower 接收到提案后,会先将提案的 zxid 与本地记录的事务日志中的最大的 zxid 进行比较。若当前提案的 zxid 大于最大 zxid,则将当前提案记录到本地事务日志中,并 向 Leader 返回一个 ACK。

当 Leader 接收到过半的 ACKs 后,对于之前回复过 Leader 的 Follower,Leader 会向其 发送 COMMIT 消息,批准这些 Follower 在本地执行该消息;对于之前未回复过 Leader 的 Follower,Leader 会将这些 Follower 对应的队列中的提案发送给这些 Follower,发送 的同时会携带 COMMIT 消息。当 Follower 收到 COMMIT 消息后,就会执行该消息。

2.6 恢复模式的两个原则

当集群正在启动过程中,或 Leader 与超过半数的主机断连后,集群就进入了恢复模式。对于要恢复的数据状态需要遵循两个原则。

(1) 新的 Leader 先将自身拥有而并非所有 Follower 都有的 proposal 发送给 Follower,再将这些 proposal 的 COMMIT 命令发送给 Follower,以保证所有的 Follower 都保存并 执行了所有的 proposal。通过以上策略,能保证已经被处理的消息不会丢。

(2) 被丢弃的消息不能再现

当 Leader 接收到事务请求并生成了 proposal,但还未向 Follower 发送时就挂了。由于 其他 Follower 并没有收到此 proposal,即并不知道该 proposal 的存在,因此在经过恢复模 式重新选举产生了新的 Leader 后,这个事务被跳过。在整个集群尚未进入正常服务状态时, 之前挂了的 Leader 主机重新启动并注册成为了 Follower。但由于保留了被跳过的 proposal, 所以其与整个系统的状态是不一致的,需要将该 proposal 删除。

ZAB 通过设计巧妙的 zxid 实现了这一目的。一个 zxid 是 64 位,高 32 是纪元 epoch 编号,每一次选举 epoch 的值都会增一。低 32 位是事务标识 xid,每产生一个事务,该 xid 值都会增一。这样设计的好处是旧的 Leader 挂了后重启,它不会被选举为新的 Leader,因 为此时它的 zxid 肯定小于当前新的 epoch。当旧的 Leader 作为 Follower 接入新的 Leader 后,新的 Leader 会让其将所有旧的 epoch 编号的、未被 COMMIT 的 proposal 清除。

(1) 已被处理过的消息不能丢

当 Leader 收到超过半数 Follower 的 ACKs 后,就向各个 Follower 广播 COMMIT 消息, 批准各个 Server 执行该写操作事务。当各个 Server 在接收到 Leader 的 COMMIT 消息后就会 在本地执行该写操作,然后会向客户端响应写操作成功。但是如果在非全部 Follower 收到 COMMIT 消息之前 Leader 就挂了,这将导致一种后果:部分 Server 已经执行了该事务,而 部分 Server 尚未收到 COMMIT 消息,所以其并没有执行该事务。当新的 Leader 被选举出, 集群经过恢复模式后需要保证所有 Server 上都执行了那些已经被部分 Server 执行过的事务。

为了保证“已被处理过的消息不能丢”的目的,ZAB 的恢复模式使用了以下的策略:  选举拥有 proposal 最大值(即 zxid 最大) 的节点作为新的 Leader:由于所有提案被 COMMIT 之前必须有合法数量的 Follower ACK,即必须有合法数量的服务器的事务日志 上有该提案,因此,只要有合法数量的节点正常工作,就必然有一个节点保存了所有被COMMIT 消息的 proposal 状态。

(2) 被丢弃的消息不能再现

当 Leader 接收到事务请求并生成了 proposal,但还未向 Follower 发送时就挂了。由于 其他 Follower 并没有收到此 proposal,即并不知道该 proposal 的存在,因此在经过恢复模 式重新选举产生了新的 Leader 后,这个事务被跳过。在整个集群尚未进入正常服务状态时, 之前挂了的 Leader 主机重新启动并注册成为了 Follower。但由于保留了被跳过的 proposal, 所以其与整个系统的状态是不一致的,需要将该 proposal 删除。

2.7 Leader 选举算法

当集群正在启动过程中,或 Leader 与超过半数的主机断连后,集群就进入了恢复模式。 而恢复模式中最重要的阶段就是 Leader 选举。

在集群启动过程中的 Leader 选举过程(算法)与 Leader 断连后的 Leader 选举过程稍微 有一些区别,基本相同。

(1) 集群启动中的 Leader 选举

若进行 Leader 选举,则至少需要两台主机,这里以三台主机组成的集群为例。

在集群初始化阶段,当第一台服务器 Server1 启动时,其会给自己投票,然后发布自己 的投票结果。投票包含所推举的服务器的 myid 和 ZXID,使用(myid, ZXID)来表示,此时 Server1 的投票为(1, 0)。由于其它机器还没有启动所以它收不到反馈信息,Server1 的状态一直属于 Looking,即属于非服务状态。

当第二台服务器 Server2 启动时,此时两台机器可以相互通信,每台机器都试图找到 Leader,选举过程如下:

(1) 每个 Server 发出一个投票。此时 Server1 的投票为(1, 0),Server2 的投票为(2, 0),然后 各自将这个投票发给集群中其他机器。

(2) 接受来自各个服务器的投票。集群的每个服务器收到投票后,首先判断该投票的有效 性,如检查是否是本轮投票、是否来自 LOOKING 状态的服务器。

(3) 处理投票。针对每一个投票,服务器都需要将别人的投票和自己的投票进行 PK,PK 规则如下:

 优先检查 ZXID。ZXID 比较大的服务器优先作为 Leader。如果 ZXID 相同,那么就比较 myid。myid 较大的服务器作为 Leader 服务器。对于 Server1 而言,它的投票是(1, 0),接收 Server2 的投票为(2, 0)。其首先会比较两者的 ZXID,均为 0,再比较 myid,此时 Server2 的 myid 最大,于是 Server1 更新自己的投票为 (2, 0),然后重新投票。对于 Server2 而言,其无须更新自己的投票,只是再次向集群中所有 主机发出上一次投票信息即可。

(4) 统计投票。每次投票后,每一台 zkServer 都会统计投票信息,判断是否已经有过半机 器接受到相同的投票信息。对于 Server1、Server2 而言,都统计出集群中已经有两台主机接 受了(2, 0)的投票信息,此时便认为已经选出了新的 Leader,即 Server2。

(5) 改变服务器状态。一旦确定了 Leader,每个服务器就会更新自己的状态,如果是 Follower,那么就变更为 FOLLOWING,如果是 Leader,就变更为 LEADING。

(6) 添加主机。在新的 Leader 选举出来后 Server3 启动,其想发出新一轮的选举。但由于 当前集群中各个主机的状态并不是 LOOKING,而是各司其职的正常服务,所以其只能是以 Follower 的身份加入到集群中。

(2) 断连后的 Leader 选举

在 Zookeeper 运行期间,Leader 与非 Leader 服务器各司其职,即便当有非 Leader 服务 器宕机或新加入时也不会影响 Leader。但是若 Leader 服务器挂了,那么整个集群将暂停对 外服务,进入新一轮的 Leader 选举,其过程和启动时期的 Leader 选举过程基本一致。

ZAB 通过设计巧妙的 zxid 实现了这一目的。一个 zxid 是 64 位,高 32 是纪元 epoch 编号,每一次选举 epoch 的值都会增一。低 32 位是事务标识 xid,每产生一个事务,该 xid 值都会增一。这样设计的好处是旧的 Leader 挂了后重启,它不会被选举为新的 Leader,因 为此时它的 zxid 肯定小于当前新的 epoch。当旧的 Leader 作为 Follower 接入新的 Leader 后,新的 Leader 会让其将所有旧的 epoch 编号的、未被 COMMIT 的 proposal 清除。

假设正在运行的有 Server1、Server2、Server3 三台服务器,当前 Leader 是 Server2,若 某一时刻 Server2 挂了,此时便开始新一轮的 Leader 选举了。选举过程如下:

(1) 变更状态。Leader 挂后,余下的非 Observer 服务器都会将自己的服务器状态由 FOLLOWING 变更为 LOOKING,然后开始进入 Leader 选举过程。

(2) 每个 Server 会发出一个投票,仍然会首先投自己。不过,在运行期间每个服务器上 的 ZXID 可能是不同,此时假定 Server1 的 ZXID 为 111,Server3 的 ZXID 为 333;在第一轮投 票中,Server1 和 Server3 都会投自己,产生投票(1, 111),(3, 333),然后各自将投票发送给 集群中所有机器。

(3) 接收来自各个服务器的投票。与启动时过程相同。集群的每个服务器收到投票后, 首先判断该投票的有效性,如检查是否是本轮投票、是否来自 LOOKING 状态的服务器。

(4) 处理投票。与启动时过程相同。针对每一个投票,服务器都需要将别人的投票和自 己的投票进行 PK。对于 Server1 而言,它的投票是(1, 111),接收 Server3 的投票为(3, 333)。 其首先会比较两者的 ZXID,Server3 投票的 zxid 为 333 大于 Server1 投票的 zxid 的 111,于是 Server1 更新自己的投票为(3, 333),然后重新投票。对于 Server3 而言,其无须更新自己的投 票,只是再次向集群中所有主机发出上一次投票信息即可。

(5) 统计投票。与启动时过程相同。经过票数统计,最终 Server3 当选新的 Leader。

(6) 改变服务器的状态。与启动时过程相同。一旦确定了 Leader,每个服务器就会更新 自己的状态。Server1 变更为 FOLLOWING,Server3 变更为 LEADING。

2.8 恢复模式下的数据同步

当完成 Leader 选举后,就要进入到恢复模式下的数据同步阶段。Leader 服务器会为每 一个 Follower 服务器准备一个队列,并将那些没有被各个 Follower 服务器同步的事务以 Proposal 的形式逐条发给各个 Follower 服务器,并在每一个 Proposal 后都紧跟一个 commit 消息,表示该事务已经被提交,Follower 可以直接接收并执行。当 Follower 服务器将所有尚 未同步的事务 proposal 都从 leader 服务器同步过来并成功执行后,会向准 leader 发送 ACK 信息。leader 服务器在收到该 ACK 后就会将该 follower 加入到真正可用的 follower 列表。

  • 3
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值