Nivelle 开拓视野冲破艰险看见世界 身临其境贴近彼此感受生活

Zookeeper学习(二)之zk基础

2016-04-12

Zookeeper介绍

Zk为分布式应用提供了高效且可靠的分布式协调服务,提供了诸如统一命名服务、配置管理和分布式锁等分布式的基础服务。在解决分布式数据一致性方面,Zk并没有直接采用Paxos算法,而是采用了一种被称为ZAB的一致性协议。ZK是一个典型的分布式数据一致性的解决方案,分布式应用程序可以基于它实现诸如数据发布/订阅、负载均衡、命名服务、分布式协调/通知、集群管理、Master选举、分布式锁和分布式队列等功能。zk保证如下分布式一致性特性。

  • 顺序一致性

从同一个客户端发起的事务请求,最终将会严格地按照其发起顺序被应用到zk中去。

  • 原子性

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

  • 单一视图

无论客户端连接的是哪个Zk服务器,其看到的服务端数据模型都是一致的。

  • 可靠性

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

  • 实时性

通常人们看到实时性的第一反应是,一旦一个事务被成功应用,那么客户端能够立即从服务端上读取到这个事务变更后的最新数据状态。

Zookeeper的设计目标

致力于提供一个高性能、高可用,且有严格的顺序访问控制能力的分布式协调服务。

目标一:简单数据模型

zk使得分布式程序能够同过一个共享的、树型结构的名字空间来进行相互协调。树型结构的名字空间:是指zk服务器内存中的一个数据模型,其由一系列被称为ZNode的数据节点组成,总的来说,其数据模型类似于一个文件系统,而ZNode之间的层级关系,就像文件系统的目录结构一样。不过和传统的磁盘文件系统不同的是,zk将全量数据存储在内存中,以此来实现提高服务器吞吐、减少延迟的目的。

目标二:可以构建集群

一个Zk集群通常由一组机器组成,一般3~5台机器就可以组成一个可用的zk集群了。

image

组成zk集群的每台机器都会在内存中维护当前的服务器状态,并且每台机器之间都互相保持着通信。值得一提的是,只要集群中存在超过一半的机器能够正常工作,那么整个集群就能够正常对外服务。

Zk的客户端程序会选择和集群中任意一台机器来共同来创建一个TCP连接,而一旦客户端和某台Zk服务器之间的连接断开后,客户端会自动连接到集群中的其他机器。

目标三:顺序访问

对于来自客户端的每个更新请求,zk都会分配一个全局唯一的递增编号,这个编号反映了所有事务操作的先后顺序,应用程序可以使用zk的这个特性来实现更高层次的同步原语。

目标四:高性能

由于zk将全量数据存储在内存中,并直接服务于客户端的所有非事务请求,因此它尤其适用于以读操作作为主的应用场景。


ZooKeeper的基本概念

集群角色:Leader、Follower和Observer三种角色。Zk集群中的所有机器通过一个Leader选举过程来选定一台被称为“Leader”的机器,Leader服务器为客户端提供读和写服务。除Leader外,其他机器包括Follower和Observer。Follower和Observer都能够提供读服务,唯一区别在于,Observer机器不参与Leader选举过程,也不参与写操作的“过半写成功”策略。因此Observer可以在不影响写性能的情况下提升集群的读性能。

会话(Session)

Session是指客户端会话。在Zk中,一个客户端连接是指客户端和服务器之间的一个TCP长连接。zk对外的服务器端默认是2181,客户端启动的时候,首先会与服务器建立一个TCP连接,从第一次建立连接开始,客户端会话的生命周期也开始了,通过这个连接,客户端能够通过心跳检测与服务器保持有效的会话,也能够向zk服务器发送请求并接受响应,同时还能通过该连接,接收来自服务器的Watch事件通知。

Session的sessionTimeout值用来设置一个客户端会话的超时时间,由于服务器压力大、网络故障或是客户端主动断开连接等各种原因导致客户端连接断开时,只要在sessionTimeout规定的时间内能够重新连接上集群中任意一台服务器,那么之前创建的会话任然有效。

数据节点(Znode)

分布式中,通常说的节点指组成集群的每一台机器。然而,在ZK中,节点分为两类。第一类同样是指构成集群的机器,我们称为机器节点;第二类则是指数据模型中的数据单元,我们称之为数据节点————ZNode。zk将所有数据存储存储在内存中,数据模型是一棵树(ZNode Tree),由斜杠(/)进行分割的路径,就是一个ZNode,例如/foo/path1。每个ZNode 上都会保存自己的数据内容,同时还会保存一些列属性信息。

在Zk中,ZNode可以分为持久节点和临时节点两类。持久节点是指一旦这个ZNode被创建了,除非主动进行ZNode的移除操作,否则这个ZNode将一直保存在ZK上。而临时节点,它的生命周期和客户端会话绑定,一旦客户端会话失效,那么这个客户端创建的所有临时节点都被移除。

版本

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

Watcher

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

ACL

zk采用ACL(Access Control Lists)策略来进行权限控制,类似于UNIX文件系统的权限控制。zk定义了如下五种权限:

  • CREATE:创建子节点的权限。
  • READ:获取节点数据和子节点列表的权限。
  • WRITE:更新节点数据的权限。
  • DELETE:删除子节点的权限。
  • ADMIN:设置节点ACL的权限。

ZAB协议(原子广播协议)

核心:ZAB协议是为分布式协调服务zk专门设计的一种支持崩溃恢复的原子广播协议。

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

image

协议介绍

有两种基本模式,分别是崩溃恢复和消息广播。当整个服务框架在启动过程中,或是当Leader服务器出现网络中断、崩溃退出与重启等异常情况时,ZAB协议就会进入恢复模式并选举产生新的Leader服务器。当选举产生了新的Leader服务器,同时集群中已经有过半的机器与该Leader服务器完成了同步之后,ZAB协议就会退出恢复模式。同步是指:数据同步,用来保证集群中存在过半的机器能够和Leader服务器的数据状态保持一致。

当集群中已经有过半的Follower服务器完成了和Leader服务器的状态同步,那么整个服务框架就可以进入消息广播模式了。 当一台同样遵守ZAB协议的服务器启动后加入到集群中时,如果此时集群中已经存在一个Leader服务器负责进行消息广播,那么新加入的服务器就会自觉地进入数据恢复模式:找到Leader服务器并与其器进行数据同步,然后一起自觉进行数据恢复模式:找到Leader所在的服务器,并与其进行数据同步,然后一起参与到消息广播过程中去。

Leader服务器在接收到客户端的事务请求后,会生成对应的事务提案并发起一轮广播协议;而如果集群中的其他机器接收到客户端的事务请求,那么这些非Leader服务器会首先将这个事务请求转发给Leader服务器。

当Leader服务器出现崩溃退出或者集群重启,或者集群中已经不存在过半的服务器与该Leader服务器保持正常通信时,那么在重新开始新一轮的原子广播事务操作之前,所有进程首先会使用崩溃恢复协议来使彼此达到一个一致的状态,于是整个ZAB流程就会从消息广播模式进入到崩溃恢复模式。


消息广播

针对客户端的事务请求,Leader服务器会为其生成对应的事务Proposal,并将其发送给集群中的其余所有机器,然后再分别收集各自的选票,最后进行事务提交。

image

Leader服务器会为每个事务请求生成对应的Proposal来进行广播,并且在广播事务Proposal之前,Leader服务器会首先为这个事务Proposal分配一个全局单调递增的唯一ID,我们称之为事务ID(即ZXID).由于zab协议需要保证每一个消息严格的因果关系,因此必须将每一个事务Proposal按照其ZXID的先后顺序来进行排序与处理。

消息广播时,Leader服务器会为每个Follower服务器都分配一个单独的对垒,然后将需要广播的事务Proposal依次放到这些队列中,并且根据FIFO策略进行消息发送。每个Follower服务器在接收到这个事务Proposal之后,都会首先将其以事务日志形式写入到本地磁盘中,并且在成功写入后反馈给Leader服务器一个ACK响应。当Leader服务器接收到超过半数的Follower的Ack响应后,就会广播一个Commit消息给所有给所有Follower服务器以通知其进行事务提交,同时Leader自身也会完成对事务的提交,而每一个Follower服务器接收到Commit消息后,也会完成对事务的提交。

崩溃恢复

一旦Leader服务器出现崩溃,或者说由于网络原因导致Leader服务器失去了与过半Follower的联系,那么就会进入崩溃恢复模式。然后则选举出一个新的Leader服务器。

基本特性

ZAB协议规定了如果一个事务Proposal在一台机器上被处理成功,那么应该在所有机器上被处理成功,哪怕机器出现故障崩溃。

ZAB协议需要确保那些已经在Leader服务器上提交的事务最终被所有服务器都提交。

ZAB协议需要确保丢弃那些只在Leader服务器上被提出的事务。

选举出来的Leader服务器拥有集群中所有机器最高编号(即ZXID最大)的事务Proposal,那么就可以保证这个新选举出来的Leader一定具有所有已经提交的提案。更为重要的是,如果让具有最高编号事务Proposal的机器来成为Leader,就可以省去Leader服务器检查Proposal的提交和丢弃工作这一步操作了。

数据同步

Leader服务器需要确保所有的Follower服务器能够接收到每一条事务Proposal,并且能够正确地将所有已经提交了的事务Proposal应用到内存数据库中去。具体的,Leader服务器会为没一个Follower服务器都准备一个队列,并将那些没有被各Follower服务器同步的事务以Proposal消息的形式逐个发送给Follower服务器,并在每一个Proposal消息后面紧接着再发送一个Commit消息,以表示该事务已经被提交。等到Follower服务器将所有其尚未同步的事务Proposal都从Leader服务器上同步过来并成功应用到本地数据库中后,Leader服务器就会将该Follower服务器加入到真正的可用Follower列表中,并开始之后的其他流程。


ZXID设计:高32位代表了Leader周期epoch的编号,每当选举产生一个新的Leader服务器,就会从这个Leader服务器上取出本地日志中最大事务Proposal的ZXID,并从该ZXID中解析出对应的epoch值,然后再对其进行加1操作,之后就会以此编号作为新的epoch,并将低32位置0来开始生成新的ZXID。低32位可以看作是一个简单的单调递增的计数器,针对客户端的每一个事务请求,Leader服务器在产生一个新的事务Proposal的时候,都会对该计数器进行加1操作。


内容来自《从Paxos到Zookeeper分布式一致性原理与实践》 作者:倪超


下一篇 dubbo原理学习

评论