文章目录

  • ​​zookeeper设计目标​​
  • ​​核心概念​​
  • ​​集群角色​​
  • ​​会话(Session)​​
  • ​​数据节点(Znode)​​
  • ​​版本​​
  • ​​Watcher​​
  • ​​ACL​​
  • ​​zab协议​​
  • ​​zab协议注意事项​​

zookeeper设计目标

ZooKeeper 致力于提供一个高性能、高可用,且具有严格的顺序访问控制能力(主要是写操作的严格顺序性)的分布式协调服务。高性能使得ZooKeeper能够应用于那些对系统吞吐有明确要求的大型分布式系统中,高可用使得分布式的单点问题得到了很好的解决,而严格的顺序访问控制使得客户端能够基于 ZooKeeper 实现一些复杂的同步原语。
下面我们来具体看一下ZooKeeper的四个设计目标。
目标一:简单的数据模型

ZooKeeper使得分布式程序能够通过一个共享的、树型结构的名字空间来进行相互协调。这里所说的树型结构的名字空间,是指ZooKeeper服务器内存中的一个数据模型,其由一系列被称为ZNode的数据节点组成,总的来说,其数据模型类似于一个文件系统,而ZNode之间的层级关系,就像文件系统的目录结构一样。不过和传统的磁盘文件系统不同的是,ZooKeeper 将全量数据存储在内存中,以此来实现提高服务器吞吐、减少延迟的目的。
目标二:可以构建集群
一个 ZooKeeper 集群通常由一组机器组成,一般 3~5 台机器就可以组成一个可用的ZooKeeper集群了。组成ZooKeeper集群的每台机器都会在内存中维护当前的服务器状态,并且每台机器之间都互相保持着通信。值得一提的是,只要集群中存在超过一半的机器能够正常工作,那么整个集群就能够正常对外服务。 ZooKeeper的客户端程序会选择和集群中任意一台机器共同来创建一个TCP连接,而一旦客户端和某台ZooKeeper服务器之间的连接断开后,客户端会自动连接到集群中的其他机器。
目标三:顺序访问
对于来自客户端的每个更新请求,ZooKeeper 都会分配一个全局唯一的递增编号,这个编号反映了所有事务操作的先后顺序,应用程序可以使用ZooKeeper的这个特性来实现更高层次的同步原语。关于 ZooKeeper 的事务请求处理和事务 ID 的生成,将会在 7.8节中做详细阐述。
目标四:高性能
由于ZooKeeper将全量数据存储在内存中,并直接服务于客户端的所有非事务请求,因此它尤其适用于以读操作为主的应用场景。作者曾经以3台3.4.3版本的ZooKeeper服务器组成集群进行性能压测,100%读请求的场景下压测结果是12~13W的QPS。

核心概念

集群角色

通常在分布式系统中,构成一个集群的每一台机器都有自己的角色,最典型的集群模式就是 Master/Slave 模式(主备模式)。在这种模式中,我们把能够处理所有写操作的机器称为Master机器,把所有通过异步复制方式获取最新数据,并提供读服务的机器称为Slave机器。 而在ZooKeeper中,这些概念被颠覆了。它没有沿用传统的Master/Slave概念,而是引入了 Leader、Follower 和 Observer 三种角色。ZooKeeper 集群中的所有机器通过一个Leader选举过程来选定一台被称为“Leader”的机器,Leader服务器为客户端提供读和写服务。除 Leader外,其他机器包括 Follower 和 Observer。Follower和 Observer都能够提供读服务,唯一的区别在于,Observer机器不参与Leader选举过程,也不参与写操作的“过半写成功”策略

会话(Session)

Session 是指客户端会话,在讲解会话之前,我们首先来了解一下客户端连接。在ZooKeeper中,一个客户端连接是指客户端和服务器之间的一个TCP长连接。ZooKeeper对外的服务端口默认是2181,客户端启动的时候,首先会与服务器建立一个TCP连接,从第一次连接建立开始,客户端会话的生命周期也开始了,通过这个连接,客户端能够通过心跳检测与服务器保持有效的会话,也能够向ZooKeeper服务器发送请求并接受响应,同时还能够通过该连接接收来自服务器的Watch事件通知。Session的sessionTimeout值用来设置一个客户端会话的超时时间。当由于服务器压力太大、网络故障或是客户端主动断开连接等各种原因导致客户端连接断开时,只要在sessionTimeout规定的时间内能够重新连接上集群中任意一台服务器,那么之前创建的会话仍然有效。

数据节点(Znode)

在谈到分布式的时候,我们通常说的“节点”是指组成集群的每一台机器。然而,在ZooKeeper中,“节点”分为两类,第一类同样是指构成集群的机器,我们称之为机器节点;第二类则是指数据模型中的数据单元,我们称之为数据节点——ZNode。ZooKeeper将所有数据存储在内存中,数据模型是一棵树(ZNode Tree),由斜杠(/)进行分割的路径,就是一个Znode,例如/foo/path1。每个ZNode上都会保存自己的数据内容,同时还会保存一系列属性信息。 在ZooKeeper中,ZNode可以分为持久节点和临时节点两类。所谓持久节点是指一旦这个ZNode被创建了,除非主动进行ZNode的移除操作,否则这个ZNode将一直保存在ZooKeeper 上。而临时节点就不一样了,它的生命周期和客户端会话绑定,一旦客户端会话失效,那么这个客户端创建的所有临时节点都会被移除。另外,ZooKeeper 还允许用户为每个节点添加一个特殊的属性:SEQUENTIAL。一旦节点被标记上这个属性,那么在这个节点被创建的时候,ZooKeeper 会自动在其节点名后面追加上一个整型数字,这个整型数字是一个由父节点维护的自增数字。

版本

在前面我们已经提到,ZooKeeper 的每个 ZNode 上都会存储数据,对应于每个 ZNode,ZooKeeper都会为其维护一个叫作Stat的数据结构,Stat中记录了这个ZNode的三个数据版本,分别是version(当前ZNode的版本)、cversion(当前ZNode子节点的版本)和aversion(当前ZNode的ACL版本)

Watcher

Watcher(事件监听器),是 ZooKeeper 中的一个很重要的特性。ZooKeeper 允许用户在指定节点上注册一些 Watcher,并且在一些特定事件触发的时候,ZooKeeper 服务端会将事件通知到感兴趣的客户端上去,该机制是ZooKeeper实现分布式协调服务的重要特性。

ACL

ZooKeeper采用ACL(Access Control Lists)策略来进行权限控制,类似于UNIX文件系统的权限控制。ZooKeeper定义了如下5种权限。
· CREATE:创建子节点的权限。
· READ:获取节点数据和子节点列表的权限。
· WRITE:更新节点数据的权限。
· DELETE:删除子节点的权限。
· ADMIN:设置节点ACL的权限。 其中尤其需要注意的是,CREATE和DELETE这两种权限都是针对子节点的权限控制。

zab协议

ZAB 协议是为分布式协调服务 ZooKeeper 专门设计的一种支持崩溃恢复的原子广播协议。ZAB协议的开发设计人员在协议设计之初并没有要求其具有很好的扩展性,最初只是为雅虎公司内部那些高吞吐量、低延迟、健壮、简单的分布式系统场景设计的。在ZooKeeper的官方文档中也指出,ZAB协议并不像Paxos算法那样,是一种通用的分布式一致性算法,它是一种特别为ZooKeeper设计的崩溃可恢复的原子消息广播算法。

在ZooKeeper中,主要依赖ZAB协议来实现分布式数据一致性,基于该协议,ZooKeeper实现了一种主备模式的系统架构来保持集群中各副本之间数据的一致性。
具体的,ZooKeeper使用一个单一的主进程来接收并处理客户端的所有事务请求,并采用ZAB的原子广播协议,将服务器数据的状态变更以事务Proposal的形式广播到所有的副本进程上去。ZAB协议的这个主备模型架构保证了同一时刻集群中只能够有一个主进程来广播服务器的状态变更,因此能够很好地处理客户端大量的并发请求。另一方面,考虑到在分布式环境中,顺序执行的一些状态变更其前后会存在一定的依赖关系,有些状态变更必须依赖于比它早生成的那些状态变更,例如变更C需要依赖变更A和变更B。这样的依赖关系也对ZAB协议提出了一个要求:ZAB协议必须能够保证一个全局的变更序列被顺序应用,也就是说,ZAB协议需要保证如果一个状态变更已经被处理了,那么所有其依赖的状态变更都应该已经被提前处理掉了。最后,考虑到主进程在任何时候都有可能出现崩溃退出或重启现象,因此,ZAB协议还需要做到在当前主进程出现上述异常情况的时候,依旧能够正常工作。

ZAB 协议的核心是定义了对于那些会改变 ZooKeeper 服务器数据状态的事务请求的处理方式,即: 所有事务请求必须由一个全局唯一的服务器来协调处理,这样的服务器被称为 Leader服务器,而余下的其他服务器则成为 Follower 服务器。Leader 服务器负责将一个客户端事务请求转换成一个事务 Proposal(提议),并将该 Proposal 分发给集群中所有的Follower 服务器。之后 Leader 服务器需要等待所有 Follower 服务器的反馈,一旦超过半数的 Follower 服务器进行了正确的反馈后,那么 Leader 就会再次向所有的 Follower服务器分发Commit消息,要求其将前一个Proposal进行提交。

总的来说,ZAB协议提交事务提案的过程跟Paxos有点类似,都是由Leader发送给下面的Follower,让Follower进行投票表决,都是超过半数以上才会通过。但是ZAB协议比Paxos多的是崩溃恢复模式,也就是Leader崩溃时,能够自我恢复。所以ZAB协议跟Paxos算法最主要的区别就是:两个设计的目标不同,ZAB协议主要用于构建一个高可用的分布式数据的主备系统,因为有崩溃恢复,Leader崩溃能够重新选举,达到一个高可用的目的。而Paxos算法目的在于构建一个分布式数据一致性系统,强调的是数据的一致性,当Proposer提议者崩溃时不能自我恢复,从而丢失高可用的功能。

zab协议注意事项

如果leader服务器出现崩溃或者由于网络原因导致leader服务器失去了与过半的follower的联系,就会进入崩溃恢复模式。在这里就会有两种情况,假设1:Leader 在复制数据给所有 Follwer 之后,还没来得及收到Follower的ack返回就崩溃,
假设2:Leader 在收到 ack 并提交了自己,同时发送了部分 commit 出去之后崩溃怎么办?
针对这些问题,ZAB 定义了 2 个原则:

  1. ZAB 协议确保丢弃那些只在 Leader 提出/复制,但没有提交的事务。
  2. ZAB 协议确保那些已经在 Leader 提交的事务最终会被所有服务器提交。

假设初始的 Leader 服务器 Server1 在提出了一个事务Proposal3 之后就崩溃退出了,从而导致集群中的其他服务器都没有收到这个事务Proposal。于是,当 Server1 恢复过来再次加入到集群中的时候,ZAB 协议需要确保丢弃Proposal3这个事务。
假设一个事务在 Leader 服务器上被提交了,并且已经得到过半 Follower 服务器的Ack反馈,但是在它将Commit消息发送给所有Follower机器之前,Leader服务器挂了,针对这种情况,ZAB协议就需要确保事务Proposal2最终能够在所有的服务器上都被提交成功,否则将出现不一致。

也可概括为:zab协议需要确保那些已经在leader服务器上提交的事务最终被所有服务器提交。zab协议需要确保丢弃那些只在leader服务器上被提出的事务