6. Zookeeper精要-内部工作原理

分布式系统要使用Zookeeper服务,就必须使用Zookeeper的Client libraries的API,对于大多数的编程语言,Zookeeper都有绑定的Client libraries可供使用,Client libraries的作用就是集成分布式应用程序和和Zookeeper服务。

下图就是一个集成了Zookeeper服务的应用程序:
6. Zookeeper精要-内部工作原理

Zookeeper服务可以运行两种模式:即Standalone模式和quorum模式,单例模式指单独的一个Zookeeper服务;Quorum模式指的是Zookeeper服务运行在一个集群中,集群中每台机器的数据通过复制模式(replicated mode)同步。

1. quorum模式

一个Zookeeper quorum由大多数的复制节点构成,在集群的所有服务器中,这些节点存储了ZooKeeper服务的最新状态。他基本上是服务器节点的最小数值且必须运行并可用于客户端请求。客户端对Zookeeper树进行的任何更新都必须持久化存储到节点的quorum中,以便能够成功的完成事务。

例如:在有5个节点的集群中,任意的两台机器失效,我们还有一个由3台机器组成的quorum,Zookeeper服务任然能够工作,一旦另外失效的2个节点**,它们就可以从已经存在的quorum中获取状态来同步自身的Zookeeper服务状态。

在Zookeeper服务中,服务节点的数量对于服务是否能正常使用是非常重要的,所有事物的提交都依赖多数一致的概念,要很好的达到多数一致,推荐在Zookeeper集群的机器数量为奇数。

我们通过一个例子来解释为什么我们需要将机器数目设置为奇数。

  1. 假设我们有一个由5台机器构成的Zookeeper集群,如果任意两台机器失效,集群任然能够工作,因为剩下的3台机器能够构成一个quorum。因此5台机器的集群的容错数为2个节点。

  2. 假设我们有一个由6台机器构成的Zookeeper集群,Zookeeper服务的容错数任然只是两个节点。这是因为如果有三个节点失效,剩下的机器就不能构成一个quorum,Zookeeper的quorums必须保证任何被客户端应答成功的事务都应该通过形成quorum的节点持久化和可见。

如果Zookeeper的quorum没有通过集群中大多数的节点形成,在Zookeeper命名空间中的状态就会不一致,将会导致不正确的结果。除了节点故障,集群中的节点之间网络分区也可能会导致操作不一致,因为quorum成员无法在他们之间进行更新。这也会造成在分布式集群中很常见的一个问题,我们称之为脑裂(split-brain).

脑裂(split-brain)指的是在集群中的机器被分成两部分且这两部分功能独立的场景。这种场景将会导致整个Zookeeper服务的状态不一致,从而会导致同一个客户端请求会获取到不同的结果,结果的获得完全取决于客户端所连接到的服务器。将Zookeeper集群节点设置为奇数,我们就可以减少发生类似错误的概率。

2. 客户端和Zookeeper服务建立Session

客户端可以通过已经配置好的Zookeeper集群的服务器列表连接到Zookeeper,客户端从列表中随机抽出一台机器并尝试连接,如果连接失败,客户端将会尝试连接另外一台机器,这个过程将会一直持续到列表中所有的机器都被尝试或者连接成功建立。

一旦客户端和服务器的链接建立,客户端和服务器的一个Session就会被创建。Zookeeper的操作中Session的概念是非常重要的,Session关联了客户端和Zookeeper服务端执行的的任何一个操作。

3. 事务的实现

在Zookeeper集群的服务器之间,一台服务器被选为Leader,其他的机器就作为Follower,Leader处理所有Zookeeper服务的变更请求。Follower接受Leader的更新意图,通过多数一致性机制,一致性状态通过集群维护。Zookeeper服务负责替换失效的Leader和同步Leader的Follower,整个过程对于客户端应用程序来说是一个完整的事务。

Zookeeper服务依靠复制机制确保所有的更新都能够被持久化到构成集群的所有服务器中,集群中的每一台机器都维护一个in-core数据库,该数据库存储了Zookeeper命名空间的整个状态。为了确保更新的持久化且在服务器崩溃的情况下可恢复,更新操作会被记录到本地磁盘。写入操作在应用到内存数据库之前也会被序列化到磁盘。

Zookeeper使用了一个称之为Zookeeper Atomic Broadcast(ZAB)的特殊原子消息协议,这种协议确定了在集群中本地数据的复制不会出差错,ZAB协议是原子性的,因此协议保证了更新要么全部成功,要么全部失败。

数据库复制和atomic broadcast协议以及Leader的选举机制构成了Zookeeper服务的核心。在Zookeeper服务名称孔家的更新也写入以及读取,都是通过这个核心组件去控制。
6. Zookeeper精要-内部工作原理

在Zookeeper的实现中,如exists, getData和getChildren请求都是在客户端所连接的服务器的本地处理,这使得在Zookeeper中读操作都非常的快。如create、delete和setDate等写和更新请求都会被转发给集群中的Leader,Leader获取到客户端请求并作为事务处理,这个事务类似于数据库管理系统中事务的概念。

一个Zookeeper事务由很多请求步骤组成一个单独的工作单元并成功的执行,事务的更新具有原子性。事务满足独立属性,事物之间无关联。Zookeeper服务中的市区是幂等的,事务通过一个事务标识符(zxid)来标识,该事物标识符是一个被分为两部分的64位整数:epoch和counter,每一部分32位。

在Zookeeper服务中事务牵涉到两部分:Leader的选举和atomic broadcast。有点类似于两阶段(two-phase)提交.

4、Leader选举

Zookeeper集群中的机器选举master服务器的过程,我们称之为Leader选举,选举出来的master服务器,我们称之为Leader,集群中除Leader之外的机器我们就称之为Follower。

参与到Leader选举算法中的每台机器都有一个状态,我们称之为LOOKING状态,如果集群中的Leader已经存在,新的机器加入集群时就会被通知Leader已经存在,在得知Leader已经存在之后,新加入的服务器就会同步Leader的数据和状态。

当集群中不存在Leader的时候,Zookeeper就会在集群中运行Leader的选举算法。在这种情况下,从算法运行开始,集群中所有的机器都会被置为LOOKING状态,算法就会发出命令让服务器交换信息选择Leader。当集群中所有参与Leader选举的机器通过数据交换达到共识选出一台机器之后,算法就停止,选出的机器就变成Leader。赢得Leader选举的机器就会将自己的状态置为LEADING,集群中其他机器的状态就被置为FOLLOWING。

集群中参与Leader选举的同等级的机器在数据交换时,所交换的数据包括服务器的id(sid)和最近执行的事务id(zxid)。每一台参与的机器,根据接受到的同等级机器的消息,将自己的服务器的id(sid)和最近执行的事务id(zxid)与接收到的消息中的服务器的id(sid)和最近执行的事务id(zxid)作比较。如果接受到的zxid大于机器自身维护的zxid,机器就承认接收到的zxid,否则,它将会设置自己的zxid并通知集群中的其他机器。

算法结束后,zxid最大的机器赢得了Leader选举算法,算法完成后,Follower服务器就会同步所选举出来的Leader的状态。

Leader选举的下一步是Leader的**,新选举出来的Leader会提出一个NEW_LEADER提议,当且仅当集群中的大多数的机器(quorum)都对NEW_LEADER提议作出回应之后,Leader才能被**。新的Leader在提交NEW_LEADER提议之前不会提交新的提议。

5、原子广播(atomic broadcast)

在Zookeeper中,所有的写请求都被转发给Leader,然后Leader向集群中所有的Follower广播写请求的更新。只有在集群中大多数的Follower响应之后Leader才会提交更新,集群中Follower才会持久化变更。Zookeeper使用ZAB协议达成一致,ZAB协议的设计师原子性的,因此,一次更新要么全部成功,要么全部失败。一旦Leader失效,集群中的其他服务器就会启动Leader选举算法在他们之间选举出一个Leader。

ZAB保证 了事务的转发和事务的提交的强一致性。事务原子信息的处理如下图所示。
6. Zookeeper精要-内部工作原理

如上图,两阶段(two-phase)提交保证了事务的一致性,在这个协议中,一旦quorum回应了事务,Leader就会提交它并且follower就会在磁盘上记录回应信息。