Zookeeper入门

一、是什么
  Zookeeper 作为一个分布式高可用、高性能的开源协调服务框架,主要用来解决分布式集群中应用系统的一致性和多个进程间的同步控制问题,分布式应用程序可以基于 Zookeeper 实现诸如数据发布/订阅、负载均衡、命名服务、分布式协调/通知、集群管理、Master 选举、分布式锁和分布式队列等功能。
Zookeeper提供基于类似于文件系统的目录节点树方式的数据存储,但是 Zookeeper 并不是用来专门存储数据的,它的作用主要是用来维护和监控你存储的数据的状态变化。通过监控这些数据状态的变化,从而可以达到基于数据的集群管理。
 
  Zookeeper设计目的
  1、最终一致性:client不论连接到哪个Server,展示给它都是同一个视图,这是Zookeeper最重要的性能。 
  2、可靠性:具有简单、健壮、良好的性能,如果消息被到一台服务器接受,那么它将被所有的服务器接受。 
  3、实时性:Zookeeper保证客户端将在一个时间间隔范围内获得服务器的更新信息,或者服务器失效的信息。但由于网络延时等原因,Zookeeper不能保证两个客户端能同时得到刚更新的数据,如果需要最新数据,应该在读数据之前调用sync()接口。 
  4、等待无关(wait-free):慢的或者失效的client不得干预快速的client的请求,使得每个client都能有效的等待。 
  5、原子性:更新只能成功或者失败,没有中间状态。 
  6、顺序性:包括全局有序和偏序两种:全局有序是指如果在一台服务器上消息a在消息b前发布,则在所有Server上消息a都将在消息b前被发布;偏序是指如果一个消息b在消息a后被同一个发送者发布,a必将排在b前面。 
 
  Zookeeper工作原理
  Zookeeper 的核心是原子广播,这个机制保证了各个Server之间的同步。实现这个机制的协议叫做Zab协议。Zab协议有两种模式,它们分别是恢复模式(选主)和广播模式(同步)。当服务启动或者在领导者崩溃后,Zab就进入了恢复模式,当领导者被选举出来,且大多数Server完成了和 leader的状态同步以后,恢复模式就结束了。状态同步保证了leader和Server具有相同的系统状态。 
 
  角色
  Zookeeper集群中有Leader,Follower,Observer三种角色。同一时刻有且只会有一个 Leader,其他都是 Follower 或 Observer。Zookeeper 默认只有 Leader 和 Follower 两种角色。如果要使用 Observer 模式,在任何想变成Observer的节点的配置文件中加入:peerType=observer 并在所有 server 的配置文件中,配置成 observer 模式的 server 的那行配置追加 :observer。
  Zookeeper 集群的所有机器通过一个 Leader 选举过程来选定一台被称为Leader』的机器,Leader服务器为客户端提供读和写服务。
Follower 和 Observer 都能提供读服务,不能提供写服务。两者唯一的区别在于,Observer 机器不参与 Leader 选举过程,也不参与写操作的过半写成功策略,因此 Observer 可以在不影响写性能的情况下提升集群的读性能。
 
  会话(Session)
  Session 是指客户端会话。在 Zookeeper 中,一个客户端连接是指客户端和 Zookeeper 服务器之间的TCP长连接。
  Zookeeper 对外的服务端口默认是2181,客户端启动时,首先会与服务器建立一个TCP连接,从第一次连接建立开始,客户端会话的生命周期也开始了,通过这个连接,客户端能够通过心跳检测和服务器保持有效的会话,也能够向 Zookeeper 服务器发送请求并接受响应,同时还能通过该连接接收来自服务器的 Watch 事件通知。
  Session 的 SessionTimeout 值用来设置一个客户端会话的超时时间。当由于服务器压力太大、网络故障或是客户端主动断开连接等各种原因导致客户端连接断开时,只要在 SessionTimeout 规定的时间内能够重新连接上集群中任意一台服务器,那么之前创建的会话仍然有效。
 
  数据节点(ZNode)
  一般节点指的是组成集群的每一台机器。而Zookeeper 中的数据节点是指数据模型中的数据单元,称为 ZNode。Zookeeper 将所有数据存储在内存中,数据模型是一棵树(ZNode Tree),由斜杠(/)进行分割的路径,就是一个ZNode,如 /hbase/master,其中 hbase 和 master 都是 ZNode。每个 ZNode 上都会保存自己的数据内容,同时会保存一系列属性信息。
 
  状态信息
  每个 ZNode 除了存储数据内容之外,还存储了 ZNode 本身的一些状态信息。用 get 命令可以同时获得某个 ZNode 的内容和状态信息。在 Zookeeper 中,version 属性是用来实现乐观锁机制中的写入校验的(保证分布式数据原子性操作)。
 
  版本
  Zookeeper 的每个 ZNode 上都会存储数据,对应于每个 ZNode,Zookeeper 都会为其维护一个叫作 Stat 的数据结构,Stat 中记录了这个 ZNode 的三个数据版本,分别是 version(当前ZNode的版本)、cversion(当前ZNode子节点的版本)和 aversion(当前 ZNode 的 ACL 版本)。
 
  事务操作
  在Zookeeper中,能改变Zookeeper服务器状态的操作称为事务操作。一般包括数据节点创建与删除、数据内容更新和客户端会话创建与失效等操作。对应每一个事务请求,Zookeeper 都会为其分配一个全局唯一的事务ID,用 ZXID 表示,通常是一个64位的数字。每一个 ZXID 对应一次更新操作,从这些 ZXID 中可以间接地识别出 Zookeeper 处理这些事务操作请求的全局顺序。
 
  Watcher
  Watcher(事件监听器),是 Zookeeper 中一个很重要的特性。Zookeeper允许用户在指定节点上注册一些 Watcher,并且在一些特定事件触发的时候,Zookeeper 服务端会将事件通知到感兴趣的客户端上去。该机制是 Zookeeper 实现分布式协调服务的重要特性。
 
  ACL
  Zookeeper 采用 ACL(Access Control Lists)策略来进行权限控制。Zookeeper 定义了如下5种权限。
  CREATE: 创建子节点的权限。
  READ: 获取节点数据和子节点列表的权限。
  WRITE:更新节点数据的权限。
  DELETE: 删除子节点的权限。
  ADMIN: 设置节点ACL的权限。
 
二、有什么用
  2.1、分布式协调/通知
  Zookeeper 中特有 Watcher 注册与异步通知机制,能够很好的实现分布式环境下不同机器,甚至不同系统之间的通知与协调,从而实现对数据变更的实时处理。使用方法通常是不同的客户端都对ZK上同一个 ZNode 进行注册,监听 ZNode 的变化(包括ZNode本身内容及子节点的),如果 ZNode 发生了变化,那么所有订阅的客户端都能够接收到相应的Watcher通知,并做出相应的处理。
  Zookeeper的分布式协调/通知,是一种通用的分布式系统机器间的通信方式。主要有心跳检测;工作进度汇报。
 
  2.2、统一命名服务(Name Service)
  分布式应用中,通常需要有一套完整的命名规则,通常情况下用树形的名称结构是一个理想的选择,树形的名称结构是一个有层次的目录结构。
  在Zookeeper的文件系统里创建一个目录,即有唯一的path。在我们使用tborg无法确定上游程序的部署机器时即可与下游程序约定好path,通过path即能互相探索发现。
 
  2.3、配置管理(Configuration Management)( 数据发布与订阅)
  配置的管理在分布式应用环境中很常见,例如同一个应用系统需要多台 PC Server 运行,但是它们运行的应用系统的某些配置项是相同的,如果要修改这些相同的配置项,那么就必须同时修改每台运行这个应用系统的 PC Server,这样非常麻烦而且容易出错。
  像这样的配置信息完全可以交给 Zookeeper 来管理,将配置信息保存在 Zookeeper 的某个目录节点中,然后将所有需要修改的应用机器监控配置信息的状态,一旦配置信息发生变化,每台应用机器就会收到 Zookeeper 的通知,然后从 Zookeeper 获取新的配置信息应用到系统中。
  程序总是需要配置的,如果程序分散部署在多台机器上,要逐个改变配置就变得困难。现在把这些配置全部放到Zookeeper上去,保存在 Zookeeper 的某个目录节点中,然后所有相关应用程序对这个目录节点进行监听,一旦配置信息发生变化,每个应用程序就会收到 Zookeeper 的通知,然后从 Zookeeper 获取新的配置信息应用到系统中就好
 
  2.4、集群管理(Group Membership)
  Zookeeper 能够很容易的实现集群管理的功能,如有多台 Server 组成一个服务集群,那么必须要一个“总管”知道当前集群中每台机器的服务状态,一旦有机器不能提供服务,集群中其它集群必须知道,从而做出调整重新分配服务策略。同样当增加集群的服务能力时,就会增加一台或多台 Server,同样也必须让“总管”知道。
  Zookeeper 不仅能够帮你维护当前的集群中机器的服务状态,而且能够帮你选出一个“总管”,让这个总管来管理集群,这就是 Zookeeper 的另一个功能 Leader Election。
  所谓集群管理无在乎两点:是否有机器退出和加入、选举master。 
  对于第一点,所有机器约定在父目录GroupMembers下创建临时目录节点,然后监听父目录节点的子节点变化消息。一旦有机器挂掉,该机器与 Zookeeper的连接断开,其所创建的临时目录节点被删除,所有其他机器都收到通知:某个兄弟目录被删除,于是,所有人都知道:它上船了。
  新机器加入也是类似,所有机器收到通知:新兄弟目录加入,highcount又有了,对于第二点,我们稍微改变一下,所有机器创建临时顺序编号目录节点,每次选取编号最小的机器作为master就好。
 
  2.5、分布式锁
  分布式锁是控制分布式系统之间同步访问共享资源的一种方式。
  分布式锁又分为排他锁和共享锁两种。
  排他锁(Exclusive Locks,简称X锁),又称为写锁或独占锁。
  如果事务T1对数据对象O1加上了排他锁,那么在整个加锁期间,只允许事务T1对O1进行读取和更新操作,其他任何事务都不能在对这个数据对象进行任何类型的操作(不能再对该对象加锁),直到T1释放了排他锁。
  可以看出,排他锁的核心是如何保证当前只有一个事务获得锁,并且锁被释放后,所有正在等待获取锁的事务都能够被通知到。
 
  共享锁(Locks)
  共享锁在同一个进程中很容易实现,但是在跨进程或者在不同 Server 之间就不好实现了。Zookeeper 却很容易实现这个功能,实现方式也是需要获得锁的 Server 创建一个 EPHEMERAL_SEQUENTIAL 目录节点,然后调用 getChildren方法获取当前的目录节点列表中最小的目录节点是不是就是自己创建的目录节点,如果正是自己创建的,那么它就获得了这个锁,如果不是那么它就调用 exists(String path, boolean watch) 方法并监控 Zookeeper 上目录节点列表的变化,一直到自己创建的节点是列表中最小编号的目录节点,从而获得锁,释放锁很简单,只要删除前面它自己所创建的目录节点就行了。
  共享锁(Shared Locks,简称S锁),又称为读锁。如果事务T1对数据对象O1加上了共享锁,那么T1只能对O1进行读操作,其他事务也能同时对O1加共享锁(不能是排他锁),直到O1上的所有共享锁都释放后O1才能被加排他锁。
 
  总结:可以多个事务同时获得一个对象的共享锁(同时读),有共享锁就不能再加排他锁(因为排他锁是写锁)
 
  2.6、队列管理
  Zookeeper 可以处理两种类型的队列:
  1、同步队列,当一个队列的成员都聚齐时,这个队列才可用,否则一直等待所有成员到达。 
  2、队列按照 FIFO 方式进行入队和出队操作,如实现生产者消费者模型。 
  第一类,在约定目录下创建临时目录节点,监听节点数目是否是我们要求的数目。 
  第二类,和分布式锁服务中的控制时序场景基本原理一致,入列有编号,出列按编号。
 
三、怎么用
  后期在云服务器上搭建zk集群环境。
 
四、深入研究方向
  4.1、zk选主流程
  Zookeeper选主流程(basic paxos)
  当leader崩溃或者leader失去大多数的follower,这时候zk进入恢复模式,恢复模式需要重新选举出一个新的leader,让所有的Server都恢复到一个正确的状态。Zk的选举算法有两种:一种是基于basic paxos实现的,另外一种是基于fast paxos算法实现的。系统默认的选举算法为fast paxos。
  1、选举线程由当前Server发起选举的线程担任,其主要功能是对投票结果进行统计,并选出推荐的Server; 
  2、选举线程首先向所有Server发起一次询问(包括自己); 
  3、选举线程收到回复后,验证是否是自己发起的询问(验证zxid是否一致),然后获取对方的id(myid),并存储到当前询问对象列表中,最后获取对方提议的leader相关信息(id,zxid),并将这些信息存储到当次选举的投票记录表中; 
  4、收到所有Server回复以后,就计算出zxid最大的那个Server,并将这个Server相关信息设置成下一次要投票的Server; 
  5、线程将当前zxid最大的Server设置为当前Server要推荐的Leader,如果此时获胜的Server获得n/2 + 1的Server票数,设置当前推荐的leader为获胜的Server,将根据获胜的Server相关信息设置自己的状态,否则,继续这个过程,直到leader被选举出来。 通过流程分析我们可以得出:要使Leader获得多数Server的支持,则Server总数必须是奇数2n+1,且存活的Server的数目不得少于n+1. 每个Server启动后都会重复以上流程。在恢复模式下,如果是刚从崩溃状态恢复的或者刚启动的server还会从磁盘快照中恢复数据和会话信息,zk会记录事务日志并定期进行快照,方便在恢复时进行状态恢复。选主的具体流程图所示: 
 
  Zookeeper选主流程(fast paxos)
  fast paxos流程是在选举过程中,某Server首先向所有Server提议自己要成为leader,当其它Server收到提议以后,解决epoch和 zxid的冲突,并接受对方的提议,然后向对方发送接受提议完成的消息,重复这个流程,最后一定能选举出Leader。
 
  4.2、zk各角色的工作流程
  Leader工作流程
  1 、恢复数据; 
  2 、维持与Learner的心跳,接收Learner请求并判断Learner的请求消息类型; 
  3 、Learner的消息类型主要有PING消息、REQUEST消息、ACK消息、REVALIDATE消息,根据不同的消息类型,进行不同的处理。 
  PING 消息是指Learner的心跳信息;
  REQUEST消息是Follower发送的提议信息,包括写请求及同步请求;
  ACK消息是 Follower的对提议的回复,超过半数的Follower通过,则commit该提议;
  REVALIDATE消息是用来延长SESSION有效时间。
 
  Follower工作流程
  Follower主要有四个功能: 
  1、向Leader发送请求(PING消息、REQUEST消息、ACK消息、REVALIDATE消息); 
  2、接收Leader消息并进行处理; 
  3、接收Client的请求,如果为写请求,发送给Leader进行投票;
  4、返回Client结果。 
 
五、面试点
  5.1、如何利用 Zookeeper 实现排他锁
  定义锁:Zookeeper 上的一个 ZNode 可以表示一个锁。例如 /exclusive_lock/lock节点就可以被定义为一个锁。
  获得锁:把Zookeeper上的一个ZNode看作是一个锁,获得锁就通过创建 ZNode 的方式来实现。所有客户端都去 /exclusive_lock节点下创建临时子节点 /exclusive_lock/lock。Zookeeper 会保证在所有客户端中,最终只有一个客户端能够创建成功,那么就可以认为该客户端获得了锁。同时,所有没有获取到锁的客户端就需要到/exclusive_lock节点上注册一个子节点变更的Watcher监听,以便实时监听到lock节点的变更情况。
释放锁:因为 /exclusive_lock/lock 是一个临时节点,因此在以下两种情况下,都有可能释放锁。当前获得锁的客户端机器发生宕机或重启,那么该临时节点就会被删除,释放锁。正常执行完业务逻辑后,客户端就会主动将自己创建的临时节点删除,释放锁。
  无论在什么情况下移除了lock节点,Zookeeper 都会通知所有在 /exclusive_lock 节点上注册了节点变更 Watcher 监听的客户端。这些客户端在接收到通知后,再次重新发起分布式锁获取,即重复『获取锁』过程。
  5.2、如果通过zk解决分布式应用中的单点故障
  在分布式锁服务中,有一种最典型应用场景,就是通过对集群进行Master选举,来解决分布式系统中的单点故障。什么是分布式系统中的单点故障:通常分布式系统采用主从模式,就是一个主控机连接多个处理节点。主节点负责分发任务,从节点负责处理任务,当我们的主节点发生故障时,那么整个系统就都瘫痪了,那么我们把这种故障叫作单点故障。
  有了Zookeeper的一致性文件系统,锁的问题变得容易。锁服务可以分为两类,一个是保持独占,另一个是控制时序。 
  对于第一类,我们将Zookeeper上的一个znode看作是一把锁,通过createznode的方式来实现。所有客户端都去创建 /distribute_lock 节点,最终成功创建的那个客户端也即拥有了这把锁。用完删除掉自己创建的distribute_lock 节点就释放出锁。 
  对于第二类, /distribute_lock 已经预先存在,所有客户端在它下面创建临时顺序编号目录节点,和选master一样,编号最小的获得锁,用完删除,依次方便。
 
  传统解决方案:传统方式是采用一个备用节点,这个备用节点定期给当前主节点发送ping包,主节点收到ping包以后向备用节点发送回复Ack,当备用节点收到回复的时候就会认为当前主节点还活着,让他继续提供服务。
  Zookeeper解决方案
  1、Master启动
  在引入了Zookeeper以后我们启动了两个主节点,"主节点-A"和"主节点-B"他们启动以后,都向Zookeeper去注册一个节点。我们 假设"主节点-A"锁注册地节点是"master-00001","主节点-B"注册的节点是"master-00002",注册完以后进行选举,编号最 小的节点将在选举中获胜获得锁成为主节点,也就是我们的"主节点-A"将会获得锁成为主节点,然后"主节点-B"将被阻塞成为一个备用节点。那么,通过这 种方式就完成了对两个Master进程的调度。
  2、Master故障
  如果"主节点-A"挂了,这时候他所注册的节点将被自动删除,Zookeeper会自动感知节点的变化,然后再次发出选举,这时候"主节点-B"将在选举中获胜,替代"主节点-A"成为主节点。
  3、Master 恢复
  如果主节点恢复了,他会再次向Zookeeper注册一个节点,这时候他注册的节点将会是"master-00003",Zookeeper会感知节点的变化再次发动选举,这时候"主节点-B"在选举中会再次获胜继续担任"主节点","主节点-A"会担任备用节点。

posted @ 2018-06-21 20:59  qhj348770376  阅读(134)  评论(0编辑  收藏  举报