ZooKeeper基础

2019-09-29  本文已影响0人  伊凡的一天

ZooKeeper是一个分布式协调服务的开源框架。主要用来解决分布式集群中应用系统的一致性问题。

ZooKeeper本质上是一个分布式的小文件存储系统,提供基于类似文件系统的目录树方式的数据存储。并且可以对树中的节点进行有效管理,从而用来维护和监控你存储的数据的状态变化。通过监控这些数据状态的变化,从而可以达到基于数据的集群管理。诸如:统一命名服务、分布式配置管理、分布式消息队列、分布式锁、分布式协调等功能。

1. 基本概念

角色

一个ZooKeeper集群中的机器角色分为3种:Leader,Follower和Observer。

会话

zookeeper 中客户端启动时会与服务器建立一个 TCP 连接,从第一次连接建立开始,客户端会话的生命周期就开始了,通过这个连接,客户端能够通过心跳检测与服务器保持有效的会话,也能够向服务器发送请求并接受响应,还能够接收来自服务器的 watch 事件通知。

节点

Zookeeper树中的每个节点被称为Znode。和文件系统的目录树一样,Zookeeper树中的每个节点可以拥有子节点。Znode具有原子性操作并且Znode的数据存储大小有限制。

每个Znode由3部分组成:

  1. stat:此为状态信息,描述该Znode的版本,权限等信息
  2. data:与该Znode关联的数据
  3. children:该Znode下的子节点

Znode有两种,分别为临时节点和永久节点。节点的类型在创建时即被确定,并且不能改变。

Znode还有一个序列化的特性,如果创建的时候指定的话,该Znode的名字后面会自动追加一个不断增加的序列号。这样便会存在4种类型的Znode节点,分别对应:

我们可以通过ZooKeeper系列号临时节点来实现分布式锁。

每个Znode都包含一系列的属性,下面是一些常见的节点的属性:

2. 读写流程

下图展示了ZooKeeper的读写流程:


ZooKeeper读写流程.PNG

对于读请求,不论client连接的是哪一种角色,ZooKeeper集群都能直接返回(因为每台机器都保存着相同的数据副本)。

消息广播

而对于写请求,如果client请求的是Follower或Observer,那么写请求将被转发到Leader。下面是Leader处理写请求的流程:

  1. Leader 接收到消息请求后,将消息赋予一个全局唯一的 64 位自增 id,叫做:zxid,通过 zxid 的大小比较即可实现因果有序这一特性。
  2. Leader 通过先进先出队列(会给每个follower都创建一个队列,保证发送的顺序性,通过 TCP 协议来实现,以此实现了全局有序这一特性)将带有 zxid 的消息作为一个提案(proposal)分发给所有 follower。
  3. 当 follower 接收到 proposal,先将 proposal 写到本地事务日志,写事务成功后再向 leader 回一个 ACK。
  4. 当 leader 接收到过半Follower的ACK后,leader 就向所有 follower 发送 COMMIT 命令,并在本地执行事务提交。
  5. 当 follower 收到消息的 COMMIT 命令时,就会提交事务从而写操作生效。

上面的流程也被称为消息广播。

ZAB协议(ZooKeeper使用的分布式一致性协议)的消息广播过程使用的是一个原子广播协议,类似于一个2PC提交过程,针对每个客户端的事务请求,leader服务器会为其生成对应的事务Proposal,并将其发送给集群中其余所有的机器,然后再分别收集各自的选票,最后进行事务提交。

上面的消息广播过程在Leader宕机时可能会出现两个中间状态:

  1. 当 leader 收到合法数量 follower 的 ACKs 后,就向各个 follower 广播 COMMIT 命令,同时也会在本地执行 COMMIT 并向连接的客户端返回「成功」。但是如果在各个 follower 在收到 COMMIT 命令前 leader 就挂了,导致剩下的服务器并没有执行都这条消息。

  2. 当 leader生成 proposal后就挂了,其他 follower 并没有收到此 proposal(或者只有一小部分收到了这条proposal),那么这条消息就是执行失败的。如果之前挂了的 leader重新启动并成为了新的leader,他保留了这条proposal,就可能将这条proposal同步给其他Follower并进行提交,那么这条proposal就是一条脏数据了。

因此,在Leader宕机后,ZooKeeper(ZAB协议)会快速的进行Leader重新选举,即崩溃恢复过程。

崩溃恢复

ZAB协议规定的崩溃恢复过程必须完成以下两件事:

  1. 对于所有原Leader已经提交了的proposal,新Leader必须能够广播并提交。
  2. 对于原Leader还未广播或只部分广播成功的proposal,新Leader能够通知原Leader或者已经同步了的Follower删除从而保证集群数据的一致性。

对于要求1,ZAB协议会选举拥有 proposal最大值(即 zxid 最大)的节点作为新的Leader。此时所有的Follower会将自己的最大zxid发送给Leader,Leader对于每一个Follower,将其落后的commit发送给Follower,Follower提交后达成一致性状态。同时,对于落后的proposal,只有当半数Follower都存在这条proposal时,Leader才会将其同步给Follower,否则Leader会通知所有拥有这条proposal的Follower删除。当Follower服务器将所有尚未同步的事务proposal都从Leader服务器同步过来并成功应用到本地后,Leader服务器就会将该Follower加入到真正可用的Follower列表中。如下图所示:

Leader同步.jpg

另外,对于要求2,当Follower存在一条半数机器都不存在的proposal时,Leader会通知其删除。

同时,Zab协议中一个 zxid 是64位,高 32 是纪元(epoch)编号,每经过一次 leader 选举产生一个新的 leader,新 leader 会将 epoch 号 +1。低 32 位是消息计数器,每接收到一条消息这个值 +1,新 leader 选举后这个值重置为 0。ZAB协议通过epoch编号来区分Leader变化周期,能够有效的避免了不同的Leader错误的使用了相同的ZXID编号提出了不一样的proposal的异常情况。

崩溃恢复的流程可以概括如下:

  1. 首先集群内机器进行投票选举Leader(每台机器都存在三种状态:LOOKING,LEADING和FOLLOWING)
  2. Leader选举成功后向所有Follower进行proposal同步
  3. 同步完成后,Leader正式成为Leader开始对外提供服务

3. ZAB协议和RAFT协议的对比

相同点:

不同点:

Raft协议请参考我的文章:https://www.jianshu.com/p/d5ac9eaeab30

4. ZooKeeper一致性

首先强一致性(strong consistency)表示任何时刻,任何用户都能读取到最近一次成功更新的数据。由于ZooKeeper在写数据时超过过半Follower的ACK后就返回,因此ZooKeeper可能读取到旧数据,因此显然不是强一致性。

ZooKeeper支持顺序一致性,即来自任意特定客户端的更新都会按其发送顺序被提交。也就是说,如果一个客户端将Znode z的值更新为a,在之后的操作中,它又将z的值更新为b,则没有客户端能够在看到z的值是b之后再看到值a(如果没有其他对z的更新)。

但是有可能client首先连接已经更新为b的机器,读取出值为b,然后再次连接了更新较慢的机器读出值为a。这样就不满足顺序一致性了。ZooKeeper为了解决这个问题,使用了单一视图的概念。

单一视图:Zookeeper 会为每个消息打上递增的 zxid(zookeeper transactioin id),客户端会维护一个 lastZxid,存放最后一次读取数据对应的 zxid,当客户端连接时,节点会判断 lastZxid 是不是比自己的 zxid 更大,如果是,说明节点的数据比客户端老,拒绝连接。

参考文章:

上一篇 下一篇

猜你喜欢

热点阅读