Zookeeper和ZAB协议

298 阅读6分钟

Zookeeper和ZAB协议

Zookeeper简介

Zookeeper是一个典型的分布式数据一致性解决方案,其设计目标是将那些复杂且容易出错的分布式一致性服务封装起来,构成一个高效可靠的原语集,并以一系列简单易用的接口提供给用户使用。分布式应用程序可以基于Zookeeper实现诸如数据发布/订阅、负载均衡、命名服务、分布式协调/通知、集群管理、Master选举、分布式锁和分布式队列等功能。

核心概念

Zookeeper数据存储模型

Zookeeper数据模型是由一系列被称为ZNode的节点组成节点树,其中根节点为/。ZNode节点包含了数据date、状态stat、访问权限ACL和子节点引用children。

节点有四种类型:

  • 持久节点(persistent):节点创建后一直存在,直到被主动删除。
  • 持久有序节点(persistent_sequential):子节点有序的持久节点。
  • 临时节点(ephemeral):临时节点的生命周期和客户端会话绑定,客户端与Zookeeper断开连接后节点就会消失。临时节点只能做叶子节点,不能创建子节点。
  • 临时有序节点(ephemeral_sequential):子节点有序的临时节点。

ZNode存储自身状态的数据结构叫做stat,如下(Zookeeper版本3.6.0):

状态属性说明
cZxidcreate zxid,创建节点的zxid
ctimecreate time,节点创建时间
mZxidmodified zxid,最后一次更新的zxid
mtimemodified time,最后一次更新时间
pZxid最后一次更新子节点的zxid,只有子节点列表变更才会更新pZxid,子节点内容变更不会更新
cversion子节点版本号
dataVersion数据版本号,创建时为0,每次更新加1
aclVersionACL版本,修改ACL加1
ephemeralOwner临时节点的sessionId;持久节点此值为0
dataLength数据的长度
numChildren子节点个数

Zookeeper会为每一个事务生成一个唯一且递增长度为64位的zxid,zxid由两部分组成:低32位表示计数器(counter)、高32位标识纪元号(epoch)。epoch在当前leader成为leader时生成。

事件监听

Zookeeper常用API有create、delete、exists、getData、getChildren等,其中exists、getData、getChildren属于读操作。客户端在请求读操作的时候,可以选择是否设置Watcher,即注册特定时间的触发器。

在ZNode发生改变时,将会触发对应的事件,Zookeeper会将事件异步通知给这些客户端。

Zookeeper集群

Zookeeper集群角色

Zookeeper集群的Server分为以下三种角色:

  • Leader:Leader作为整个Zookeeper集群的主节点,负责响应所有对Zookeeper状态变更的请求。它会将每个写请求进行排序和编号,以保证整个集群内部消息处理的FIFO。
  • Follower:Follower除了响应本服务器上的读请求外,还要处理Leader的提议,并在Leader提交该提议时在本地也进行提交。
  • Observer:为客户端提供读服务,如果是写请求则转发给Leader,不参与Leader选举,不参与“过半写成功”。

Zookeeper集群中节点状态

  • Looking:当前Server不知道Leader是谁,正在搜寻。
  • Leading:当前Server即为选举出来的Leader。
  • Following:Leader已经选举出来,当前Server与之同步。
  • Observing:当前节点为Observer。

Zookeeper集群搭建注意事项

  1. Zookeeper集群搭建需要奇数个节点,最少3个节点。

  2. 需要在数据目录data下创建一个myid文件,记录该节点在集群中的id。

  3. 在zoo.cfg中添加集群配置:

    # zk集群配置
    server.1=172.16.0.2:2888:3888
    server.2=172.16.0.3:2888:3888
    server.3=172.16.0.4:2888:3888
    

    Zookeeper集群配置格式为server.A=B:C:D[:observer],A是节点myid,B是ip,C是Leader与其它节点交换信息的端口,D是选举时使用的端口。

Zab协议

Zab协议(Zookeeper Atomic Broadcast Protocol)是为Zookeeper设计的一种支持崩溃恢复的一致性协议。

Zab协议中有两种模式:崩溃恢复和消息广播。

崩溃恢复

当服务器启动时、Leader崩溃或Leader失去大多数的Follower时集群会进行崩溃恢复。

崩溃恢复分为三个阶段:

  1. 选举

    默认的选举算法是FastLeaderElection。

    此时集群节点都处于Looking状态。每个节点向其它节点发出一个投票,包含自身的myid和最近一次事务的zxid。

    接下来节点会比较自身和收到的的zxid,如果发现收到的zxid更大,就变更投票给目前已知最大zxid所属的节点,同理,zxid相同时选择myid大的。

    每次投票后,服务器都会统计投票数量,判断是否有某个节点得到半数以上的投票。如果存在这样的节点,该节点将会成为准Leader,状态变为Leading。其它节点的状态变为Following。

  2. 发现

    用于在Follower节点中发现最新的zxid和事务日志。

  3. 同步

    把Leader收集的最新事务日志,同步给集群中所有的Follower。只有当半数Follower同步成功,这个准Leader才会成为正式的Leader。

之后,集群进入消息广播模式。

消息广播

Zookeeper常规情况下更新数据的时候,由Leader广播到所有的Follower。过程如下:

  1. 客户端写请求给任意Follower;
  2. Follower把写请求转发给Leader;
  3. Leader将写请求转换为事务Proposal,并将该Proposal分发给集群中所有的Follower;
  4. Follower接到Propose消息,写入日志成功后,返回ACK给Leader;
  5. Leader接到半数以上ACK消息,返回成功给客户端,并且广播Commit请求给Follower。

Leader和每个Follower之间通过消息队列异步。

和大多数数据库系统一样,Zookeeper对于每个写操作会先WAL(Write-Ahead-Logging),再对内存中的数据做更新,然后向Client响应更新结果。定期将内存数据落盘。

常见问题

  1. 为什么Zookeeper集群要部署奇数台服务器?

    Zookeeper集群宕掉几个节点后,剩下的个数必须超过原来的半数集群才可以继续运行。无论奇偶都可以选举leader。5个节点最多宕掉2个,6个也是最多宕掉2个,2n个和2n-1个的容忍度是一样的。从节省资源的角度看,没必要部署偶数个。

  2. 脑裂问题?

    脑裂:集群由于网络分区形成了多个小集群,每个小集群都选举出各自的Master或Leader。

    Zookeeper集群有过半数可用的要求,少于半数的小集群无法选举,因此不会有脑裂问题。

  3. Zookeeper羊群效应?

    当被监控的节点发生变化时,Zookeeper会通知所有监控的客户端。如果监控的客户端数量过多,就会造成Zookeeper性能突然下降。

    因此要避免在一个特定节点设置大量的监控。