Zookeeper手册
Zookeeper手册
Zookeeper
zookeeper概念
Zookeeper 是一个分布式协调服务,可用于服务发现,分布式锁,分布式领导选举,配置管理等。 Zookeeper 提供了一个类似于 Linux 文件系统的树形结构(可认为是轻量级的内存文件系统,但只适合存少量信息,完全不适合存储大量文件或者大文件),同时提供了对于每个节点的监控与 通知机制。
Zookeeper角色
Zookeeper集群是一个基于主从复制的高可用集群,每个服务器承担如下三种角色中的一种
Leader
1. 一个 Zookeeper 集群同一时间只会有一个实际工作的 Leader,它会发起并维护与各 Follwer 及Observer间的心跳。
2. 所有的写操作必须要通过 Leader 完成再由 Leader 将写操作广播给其它服务器。只要有超过 半数节点(不包括observeer节点)写入成功,该写请求就会被提交(类 2PC 协议)。
Follower
1. 一个Zookeeper集群可能同时存在多个Follower,它会响应Leader的心跳,
2. Follower可直接处理并返回客户端的读请求,同时会将写请求转发给Leader处理,
3. 并且负责在Leader处理写请求时对请求进行投票。
Observer
角色与Follower类似,但是无投票权。Zookeeper需保证高可用和强一致性,为了支持更多的客 户端,需要增加更多 Server;Server 增多,投票阶段延迟增大,影响性能;
引入 Observer, Observer不参与投票;
Observers接受客户端的连接,并将写请求转发给leader节点;
加入更 多Observer节点,提高伸缩性,同时不影响吞吐率。
Zookeeper的数据结构
Zookeeper将数据存储于内存中,具体而言,Znode是存储数据的最小单元。而Znode被以层次化的结构进行组织,形容一棵树。其对外提供的视图类似于Unix文件系统。树的根Znode节点相当于Unix文件系统的根路径。正如Unix中目录下可以有子目录一样,Znode结点下也可以挂载子结点,最终形成如下所示结构。
ZAB协议
事务编号 Zxid
(事务请求计数器 + epoch)
在 ZAB ( ZooKeeper Atomic Broadcast , ZooKeeper 原子消息广播协议) 协议的事务编号 Zxid 设计中,Zxid 是一个 64 位的数字,其中低 32 位是一个简单的单调递增的计数器,针对客户端每 一个事务请求,计数器加 1;而高 32 位则代表 Leader 周期 epoch 的编号,每个当选产生一个新 的 Leader 服务器,就会从这个 Leader 服务器上取出其本地日志中最大事务的ZXID,并从中读取 epoch 值,然后加 1,以此作为新的 epoch,并将低 32 位从 0 开始计数。
Zxid
(Transaction id)类似于 RDBMS 中的事务 ID,用于标识一次更新操作的 Proposal(提议) ID。为了保证顺序性,该zkid必须单调递增。
epoch
epoch:可以理解为当前集群所处的年代或者周期,每个 leader 就像皇帝,都有自己的年号,所 以每次改朝换代,leader 变更之后,都会在前一个年代的基础上加 1。这样就算旧的 leader 崩溃 恢复之后,也没有人听他的了,因为 follower 只听从当前年代的 leader 的命令。
Zab协议
有两种模式 恢复模式(选主)、广播模式(同步 )
Zab协议有两种模式,它们分别是恢复模式(选主)和广播模式(同步)。当服务启动或者在领导 者崩溃后,Zab就进入了恢复模式,当领导者被选举出来,且大多数Server完成了和leader的状 态同步以后,恢复模式就结束了。状态同步保证了leader和Server具有相同的系统状态。
ZAB协议 4阶段
Leader election (选举阶段 选出准 Leader)
1. Leader election(选举阶段):节点在一开始都处于选举阶段,只要有一个节点得到超半数 节点的票数,它就可以当选准 leader。只有到达 广播阶段(broadcast) 准 leader 才会成 为真正的 leader。这一阶段的目的是就是为了选出一个准 leader,然后进入下一个阶段。
Discovery (发现阶段 接受提议、生成 epoch 、接受 epoch)
2. Discovery(发现阶段):在这个阶段,followers 跟准 leader 进行通信,同步 followers 最近接收的事务提议。这个一阶段的主要目的是发现当前大多数节点接收的最新提议,并且 准 leader 生成新的 epoch,让 followers 接受,更新它们的 accepted Epoch
一个 follower 只会连接一个 leader,如果有一个节点 f 认为另一个 follower p 是 leader,f 在尝试连接 p 时会被拒绝,f 被拒绝之后,就会进入重新选举阶段。
Synchronization (同步阶段 同步 follower副本 )
3. Synchronization(同步阶段):同步阶段主要是利用 leader 前一阶段获得的最新提议历史, 同步集群中所有的副本。只有当 大多数节点都同步完成,准 leader 才会成为真正的 leader。 follower 只会接收 zxid 比自己的 lastZxid 大的提议。
Broadcast (广播阶段-leader消息广播 )
4. Broadcast(广播阶段):到了这个阶段,Zookeeper 集群才能正式对外提供事务服务, 并且 leader 可以进行消息广播。同时如果有新的节点加入,还需要对新节点进行同步。
ZAB 提交事务并不像 2PC 一样需要全部 follower 都 ACK,只需要得到超过半数的节点的 ACK 就 可以了。
ZAB协议 JAVA
实现 ( FLE - 发现阶段 和 同步 合并为 Recovery Phase (恢复阶段) )
协议的 Java 版本实现跟上面的定义有些不同,选举阶段使用的是 Fast Leader Election(FLE), 它包含了 选举的发现职责。因为 FLE 会选举拥有最新提议历史的节点作为 leader,这样就省去了 发现最新提议的步骤。实际的实现将 发现阶段 和 同步合并为 Recovery Phase(恢复阶段)。所 以,ZAB 的实现只有三个阶段:Fast Leader Election;Recovery Phase;Broadcast Phase。
投票机制
每个sever首先给自己投票,然后用自己的选票和其他sever选票对比,权重大的胜出,使用权 重较大的更新自身选票箱。具体选举过程如下:
1. 每个Server启动以后都询问其它的Server它要投票给谁。对于其他server的询问, server每次根据自己的状态都回复自己推荐的leader的id和上一次处理事务的zxid(系 统启动时每个server都会推荐自己)
2. 收到所有Server回复以后,就计算出zxid最大的哪个Server,并将这个Server相关信 息设置成下一次要投票的Server。
3. 计算这过程中获得票数最多的的sever为获胜者,如果获胜者的票数超过半数,则改 server被选为leader。否则,继续这个过程,直到leader被选举出来
4. leader就会开始等待server连接
5. Follower连接leader,将最大的zxid发送给leader
6. Leader根据follower的zxid确定同步点,至此选举阶段完成。
7. 选举阶段完成Leader同步后通知follower 已经成为uptodate状态
8. Follower收到uptodate消息后,又可以重新接受client的请求进行服务了
实例
目前有5台服务器,每台服务器均没有数据,它们的编号分别是1,2,3,4,5,按编号依次启动,它们 的选择举过程如下:
1. 服务器1启动,给自己投票,然后发投票信息,由于其它机器还没有启动所以它收不到反 馈信息,服务器1的状态一直属于Looking。
2. 服务器2启动,给自己投票,同时与之前启动的服务器1交换结果,由于服务器2的编号 大所以服务器2胜出,但此时投票数没有大于半数,所以两个服务器的状态依然是 LOOKING。
3. 服务器3启动,给自己投票,同时与之前启动的服务器1,2交换信息,由于服务器3的编 号最大所以服务器3胜出,此时投票数正好大于半数,所以服务器3成为领导者,服务器 1,2成为小弟。
4. 服务器4启动,给自己投票,同时与之前启动的服务器1,2,3交换信息,尽管服务器4的 编号大,但之前服务器3已经胜出,所以服务器4只能成为小弟。
5. 服务器5启动,后面的逻辑同服务器4成为小弟。
Zookeeper 工作原理(原子广播)
1. Zookeeper的核心是原子广播,这个机制保证了各个server之间的同步。实现这个机制 的协议叫做Zab协议。Zab协议有两种模式,它们分别是恢复模式和广播模式。
2. 当服务启动或者在领导者崩溃后,Zab就进入了恢复模式,当领导者被选举出来,且大多 数server的完成了和leader的状态同步以后,恢复模式就结束了。
3. 状态同步保证了leader和server具有相同的系统状态
4. 一旦leader已经和多数的follower进行了状态同步后,他就可以开始广播消息了,即进 入广播状态。这时候当一个server加入zookeeper服务中,它会在恢复模式下启动,发 现leader,并和leader进行状态同步。待到同步结束,它也参与消息广播。Zookeeper 服务一直维持在Broadcast状态,直到leader崩溃了或者leader失去了大部分的 followers支持。
5. 广播模式需要保证proposal被按顺序处理,因此zk采用了递增的事务id号(zxid)来保 证。所有的提议(proposal)都在被提出的时候加上了zxid。
6. 实现中zxid是一个64为的数字,它高32位是 epoch用来标识leader关系是否改变, 每次一个leader被选出来,它都会有一个新的epoch。低32位是个递增计数。
7. 当leader崩溃或者leader失去大多数的follower,这时候zk 进入恢复模式,恢复模式 需要重新选举出一个新的leader,让所有的server都恢复到一个正确的状态。
Znode 有四种形式的目录节点
1. PERSISTENT:持久的节点。
最常见的Znode类型,一旦创建将在一直存在于服务端,除非客户端通过删除操作进行删除。持久结点下可以创建子结点。
2. EPHEMERAL:暂时的节点。
临时结点的生命周期和客户端会话保持一致。客户端段会话存在的话临时结点也存在,客户端会话断开则临时结点会自动被服务端删除。临时结点下不能创建子结点。
3. PERSISTENT_SEQUENTIAL:持久化顺序编号目录节点。
在具有持久结点基本特性的基础上,会通过在结点路径后缀一串序号来区分多个子结点创建的先后顺序。这工作由Zookeeper服务端自动给我们做,只要在创建Znode时指定结点类型为该类型。
4. EPHEMERAL_SEQUENTIAL:暂时化顺序编号目录节点。
具有临时结点的基本特性,又有顺序性。
Znode的结构
Znode结构主要由存储于其中的数据信息和状态信息两部分构成,通过get 命令获取一个Znode结点的信息如下
第一行存储的是ZNode的数据信息,从cZxid开始就是Znode的状态信息 Znode的状态信息比较多,挑几个比较重要的讲
czxid: 即Created ZXID,表示创建该Znode结点的事务ID
mzxid: 即Modified ZXID,表示最后一次更新该结点的事务ID
version 该Znode结点的版本号。每个Znode结点被创建时版本号都为0,每更新一次都会导致版本号加1,即使更新前后Znode存储的值没有变化版本号也会加1。version值可以形象的理解为Znode结点被更新的次数。Znode状态信息中的版本号信息,使得服务端可以对多个客户端对同一个Znode的更新操作做并发控制。整个过程和java中的CAS有点像,是一种乐观锁的并发控制策略,而version值起到了冲突检测的功能。客户端拿到Znode的version信息,并在更新时附上这个version信息,服务端在更新Znode时必须必须比较客户端的version和Znode的实际version,只有这两个version一致时才会进行修改。
Zookeeper的事件监听机制
Zookeeper中可以通过Watcher来实现事件监听机制。客户端可以向服务端注册Watcher用以监听某些事件,一旦该事件发生,服务端即会向客户端发送一个通知。其主要工作流程如下图所示
具体而言,Watcher是Zookeeper原生API中提供的事件监听接口,用户要实现事件监听必须实现该接口并重写process(WatchedEvent event)方法,该方法定义了客户端在接收到服务端事件通知后的回调逻辑。究竟服务端的什么事件可以被监听?按通知状态划分有SyncConnected,Disconnected,Expired,AuthFailed等好多种,这里主要讲下SyncConnected状态下的几种事件类型:
Node(-1) 客户端与服务端成功建立会话
NodeCreated(1) Watcher监听的对应Znode被创建
NodeDeleted(2) Watcher监听的Znode被删除
NodeDataChanged(3) Watcher监听的Znode的数据内容被改变,注意即使变更前后的数据内容完全一样也会触发该事件,或者理解成该事件的触发条件是Znode的版本号变更也没问题
NodeChildrenChanged(4) Watcher监听的对应Znode的子结点发生变更
Zookeeper的事件监听机制有以下特性:
1.当监听器监听的事件被触发,服务端会发送通知给客户端,但通知信息中不包括事件的具体内容。以监听ZNode结点数据变化为例,当Znode的数据被改变,客户端会收到事件类型为NodeDataChanged的通知,但该Znode的数据改变成了什么客户端无法从通知中获取,需要客户端在收到通知后手动去获取。
2.Watcher是一次性的。一旦被触发将会失效。如果需要反复进行监听就需要反复进行注册。这么设计是为了减轻服务端的压力,但是对开发者而言却是相当不友好,不过不用着急,可以通过一些Zookeeper的开源客户端轻松实现对某一事件的永久监听。
Zookeeper如何保证分布式数据一致性——ZAB协议
Zookeeper采用ZAB(Zookeeper Atomic Broadcast)协议来保证分布式数据一致性。ZAB并不是一种通用的分布式一致性算法,而是一种专为Zookeeper设计的崩溃可恢复的原子消息广播算法。ZAB协议包括两种基本模式:崩溃恢复模式和消息广播模式。崩溃恢复模式主要用来在集群启动过程,或者Leader服务器崩溃退出后进行新的Leader服务器的选举以及数据同步;消息广播模式主要用来进行事务请求的处理。下面就从这两个方面来介绍
事务请求的处理流程
ZAB协议的核心是定义了对事务请求的处理方式,整个过程可以概括如下:
1.所有的事务请求都交由集群的Leader服务器来处理,Leader服务器会将一个事务请求转换成一个Proposal(提议),并为其生成一个全局递增的唯一ID,这个ID就是事务ID,即ZXID,Leader服务器对Proposal是按其ZXID的先后顺序来进行排序和处理的。
2.之后Leader服务器会将Proposal放入每个Follower对应的队列中(Leader会为每个Follower分配一个单独的队列),并以FIFO的方式发送给Follower服务器。
3.Follower服务器接收到事务Proposal后,首先以事务日志的方式写入本地磁盘,并且在成功后返回Leader服务器一个ACK响应。
4.Leader服务器只要收到过半Follower的ACK响应,就会广播一个Commit消息给Follower以通知其进行Proposal的提交,同时Leader自身也会完成Proposal的提交。
Leader服务器的选举流程
当集群中不存在Leader服务器时集群会进行Leader服务器的选举,这通常存在于两种情况:1.集群刚启动时 2.集群运行时,但Leader服务器因故退出。集群中的服务器会向其他所有的Follower服务器发送消息,这个消息可以形象化的称之为选票,选票主要由两个信息组成,所推举的Leader服务器的ID(即配置在myid文件中的数字),以及该服务器的事务ID,事务表示对服务器状态变更的操作,一个服务器的事务ID越大,则其数据越新。整个过程如下所述:
1.Follower服务器投出选票(SID,ZXID),第一次每个Follower都会推选自己为Leader服务器,也就是说每个Follower第一次投出的选票是自己的服务器ID和事务ID。
2.每个Follower都会接收到来自于其他Follower的选票,它会基于如下规则重新生成一张选票:比较收到的选票和自己的ZXID的大小,选取其中最大的;若ZXID一样则选取SID即服务器ID最大的。最终每个服务器都会重新生成一张选票,并将该选票投出去。
这样经过多轮投票后,如果某一台服务器得到了超过半数的选票,则其将当前选为Leader。由以上分析可知,Zookeeper集群对Leader服务器的选择具有偏向性,偏向于那些ZXID更大,即数据更新的机器。
Zookeeper如何进行服务器故障的容错
Zookeeper通过事务日志和数据快照来避免因为服务器故障导致的数据丢失。
事务日志是指服务器在更新内存数据前先将事务操作以日志的方式写入磁盘,Leader和Follower服务器都会记录事务日志。
数据快照是指周期性通过深度遍历的方式将内存中的树形结构数据转入外存快照中。但要注意这种快照是"模糊"的,因为可能在做快照时内存数据发生了变化。但是因为Zookeeper本身对事务操作进行了幂等性保证,故在将快照加载进内存后会通过执行事务日志的方式来讲数据恢复到最新状态。
Zookeeper分布式锁
基于ZooKeeper分布式锁的流程
在zookeeper指定节点(locks)下创建临时顺序节点node_n
获取locks下所有子节点children
对子节点按节点自增序号从小到大排序
判断本节点是不是第一个子节点,若是,则获取锁;若不是,则监听比该节点小的那个节点的删除事件
若监听事件生效,则回到第二步重新进行判断,直到获取到锁
Zookeeper配置管理
实现思路
假如我们需要修改三(或者更多)台服务器上 redis.conf 的配置信息,如果一台一台的去修改,则会加大出错概率,而且也不实际。这时候,我们需要引入Zookeeper(下面简称zk),我们需要知道,zk 中有个 watcher 事件,包括 : EventType:NodeCreated //节点创建 EventType:NodeDataChanged //节点的数据变更 EventType:NodeChildrentChanged //子节点下的数据变更 EventType:NodeDeleted // 节点删除 当我们监听了上面的事件时,事件触发就会被告知。以统一更新 redis.conf 配置文件为例,我们可以实现监听某一个节点的数据更新事件,当DBA更改了该节点的值(一般为 json 串,方便程序解析,例:{"type":"update","url":"ftp:192.168.2.10/config/redis.xml"}),此时我们可以根据 type 的值“update”可知,是需要更新 redis.conf 配置文件,然后根据 url 的值,获取最新的 redis.conf 文件所在的服务器地址。此时,我们可以下载最新配置文件,然后删除原来的 redis.conf 配置文件,最后将最新的配置文件添加到项目中,从而通过重启程序就可以读取到最新的配置了。
Zookeeper队列管理
两种类型的队列:
1、 同步队列,当一个队列的成员都聚齐时,这个队列才可用,否则一直等待所有成员到达。
2、队列按照 FIFO 方式进行入队和出队操作。
第一类,在约定目录下创建临时目录节点,监听节点数目是否是我们要求的数目。
第二类,和分布式锁服务中的控制时序场景基本原理一致,入列有编号,出列按编号。
终于了解完我们能用zookeeper做什么了,可是作为一个程序员,我们总是想狂热了解zookeeper是如何做到这一点的,单点维护一个文件系统没有什么难度,可是如果是一个集群维护一个文件系统保持数据的一致性就非常困难了。
以上是 Zookeeper手册 的全部内容, 来源链接: utcz.com/z/512240.html