概述
ZooKeeper是一个分布式的,开源的分布式应用程序协调服务,它包含一个简单的原语集,属于Apache的顶级项目,能够基于它实现服务发现,配置维护,集群管理和分布式锁等。node
架构

经过架构图咱们看到zookeeper主要有以下四种角色:缓存
- Leader:处理读写请求,处理与Follower的心跳检测,并以事务的方式处理与Follower和Observer的数据同步
- Follower:直接处理读请求,对于写请求转发给 Leader,Leader选举中参与竞争和投票,与Leader维持数据同步
- Observer:直接处理读请求,对于写请求转发给 Leader,Leader选举中不参与投票,与Leader维持数据同步
- Client:请求发起者
概念
- znode
zk中数据都存储在znode中,一个znode节点能够包含子znode同时也能够包含数据,每一个znode都有一个惟一的访问路径。znode主要有以下四种类型:
- PERSISTENT-持久化目录节点
客户端与zookeeper断开链接后,该节点依旧存在
- PERSISTENT_SEQUENTIAL-持久化顺序编号目录节点
客户端与zookeeper断开链接后,该节点依旧存在,只是Zookeeper给该节点名称进行顺序编号
- EPHEMERAL-临时目录节点
客户端与zookeeper断开链接后,该节点被删除
- EPHEMERAL_SEQUENTIAL-临时顺序编号目录节点
客户端与zookeeper断开链接后,该节点被删除,只是Zookeeper给该节点名称进行顺序编号
- session
session是zookeeper中会话的实例载体,一个session则是指代一个客户端会话。一个会话必须包含如下几个基本的属性:
- sessionID : 会话的ID,用来惟一标识一个会话,每一次客户端创建链接的时候,zookeeper服务端都会给其分配一个全局惟一的sessionID
- timeOut:一次会话的超时时间,客户端在构造zookeeper实例的时候,会配置一个sessionTimeOut参数用于指定会话的超时的时间。zookeeper服务端会按照链接的客户端发来的timeOut参数来计算并肯定超时的时间
- tickTime:下一次会话超时的时间点,为了方便zookeeper对会话进行所谓的分桶策略进行管理,同时也能够实现高效的对会话的一个检查和清理。tickTime是一个13位的Long类型的数值,通常状况下这个值接近timeOut,可是并不彻底相等
- isCloseing:用来标记当前会话是否已经处于被关闭的状态。若是服务端检测到当前会话的超时时间已经到了,就会将isCloseing属性标记为已经关闭,这样之后即便再有这个会话的请求访问也不会被处理
- watch
用户能够对一个znode设置watch,当这个znode发生了变化时,例如建立、删除、数据变动、添加或移除子节点,watch API就会发出通知。可是,zookeeper的 watch有一个缺点,就是这个watch只能被触发一次,一旦发出了通知,若是还想对这个节点继续watch,用户须要从新设置watch。
- zxid
leader 服务器在接收到事务请求后,会为每一个事务请求生成对应的 proposal 来进行广播,而且在广播事务 proposal 以前,leader 服务器会首先为这个事务 proposal 分配一个全局单调递增的惟一 ID ,咱们称之为zxid。zxid就是zk中的事务编号,是一个8字节的整型数字,一共有64位长度,前32位用来记录epoch,后32位就是用来计数。每一次写请求都会增长后32位,每一次leader选举会增长前32位,每次增长都是+1。
- myid
启动配置zoo.cfg中有一项 dataDir 指定了数据存放的路径,在此路径下新建一个文本文件,命名为myid, 文本内容就是一个数字,这个数字就是当前节点的myid,用来惟一标识一个节点。
- 节点的状态
在zab协议中,是经过自身的状态来区分本身的角色的。在组成zab协议的全部进程启动的时候,初始化状态都是 LOOKING 状态,此时进程组中不存在leader,选举以后才有,在进行选举成功后,就进入消息广播模式,此时 zookeeper 集群中的角色状态就再也不是 LOOKING 状态。在运行期间各个进程可能出现如下三种状态之一:
- LOOING:处在这个状态时,会进入 Leader 选举状态
- FOLLOWER:Follower 服务器和 Leader 服务器保持同步时的状态
- LEADING:Leader 服务器做为主进程领导者的状态
- prososal
leader服务器将客户端事务请求转化成一个事务prososal 核心zab协议
zookeeper分布式服务可以知足CAP理论中的CP,即能保证一致性和分区容忍性,可是不是保证可用性。像在leader选举的过程当中,服务是不可用的。而zookeeper是采用zab(Zookeeper Atomic Broadcast)原子消息广播协议来保证CP。
zab在工做的过程当中,会有两种模式的切换:崩溃恢复模式和消息广播模式。服务器
- 在进入崩溃恢复模式时 zookeeper集群会进行 leader 选举,通常有两种状况会发生选举:
- 当服务器启动时期会进行 Leader 选举。
- 当服务器运行期 Leader 服务器的出现网络中断、崩溃退出、重启等异常状况,或者当集群中半数的服务器与该 Leader 服务器没法通讯时,进入崩溃恢复模式,开始 Leader 选举。
- 选举出 Leader 服务器后,会进入消息广播模式,开始接收处理客户端的请求。
恢复模式
崩溃恢复模式下 leader 选举的过程细节以下:网络
- 检测节点处于 LOOKING 阶段,开始选举 leader
发起投票时有两种状况:
- 在服务启动的初始阶段,每一个服务器都会投票给本身以(myid,zxid)的信息形式发送,那初始阶段没有 zxid 值,就会发送(myid,0)
- 在服务器运行期间,每一个服务器上的 zxid 都有值,且 zxid 都不相同,因此就正常发送(myid,zxid)
- 各节点收到信息后将收到的(myid,zxid)和本身的比较。会比较epoch、写请求操做数、myid三个字段,依次比较谁大谁就更有资格成为leader
- 而后判断是否有半数的机器投票选出 leader,若是否,在进入新一轮投票,直到选出
- 选出 leader 后,其余节点就变成 follower 角色,并向 leader 发送本身服务器的最大 zxid ,leader 服务器收到后会和本身本地的提议缓存队列进行比较,判断使用那种策略进行同步
- 当同步完成,集群就能够正常的处理请求了,就进入消息广播模式了
消息广播模式
主要过程以下:session
- leader 服务器接收到请求后在进行广播事务 proposal 以前会为这个事务分配一个 ZXID,再进行广播。
- leader 服务器会为每一个 follower 服务器都各自分配一个单独的队列,而后将须要广播的事务 proposal 依次放入这些队列中去,并根据 FIFO 策略进行消息的发送。
- 每一个follower 服务器在接收到后都会将其以事务日志的形式写入到本地磁盘中,而且在成功写入后返回leader 服务器一个 ACK 响应。
- 当有超过半数的服务器 ACK 响应后,leader 就会广播一个 commit 消息给全部的 follower 服务器,follower 接收到后就完成对事务的提交操做。
应用场景
发布订阅
做为配置中心,来存放服务的配置架构
命名服务
命名服务是指经过指定的名字来获取资源或者服务的地址,提供者的信息。简单来讲使用Zookeeper作命名服务就是用路径做为名字,路径上的数据就是其名字指向的实体,例如url地址。并发
集群管理
集群中的全部节点都注册到zk中,并添加watch机制。可以实现集群节点数量,在线状态,上下线状态等的自动发现。异步
分布式通知/协调
ZooKeeper 中特有 watcher 注册与异步通知机制,可以很好的实现分布式环境下不一样系统之间的通知与协调,实现对数据变动的实时处理。socket
master选举
利用zooKeeper的一致性,可以保证在分布式高并发状况下节点建立的全局惟一性,即:同时有多个客户端请求建立 /currentMaster 节点,最终必定只有一个客户端请求可以建立成功。利用这个特性,就能很轻易的在分布式环境中进行集群选取了。分布式
分布式锁
锁服务能够分为两类,一个是保持独占,另外一个是控制时序。
- 独占
全部客户端都去建立 /distribute_lock 节点,最终成功建立的那个客户端也即拥有了这把锁。
- 控制时序
/distribute_lock节点已预先存在,客户端在它下面建立临时有序节点(这个能够经过节点的属性控制:CreateMode.EPHEMERAL_SEQUENTIAL来指定)。zk的父节点(/distribute_lock)维持一份 sequence,保证子节点建立的时序性,从而也造成了每一个客户端的全局时序。 分布式队列
队列方面,简单地讲有两种,一种是常规的先进先出队列,另外一种是要等到队列成员聚齐以后的才统一按序执行。对于第一种先迚先出队列,和分布式锁服务中的控制时序场景基本原理一致,这里再也不赘述。
第二种队列实际上是在 FIFO 队列的基础上做了一个加强。一般能够在 /queue 这个 znode 下预先创建一个/queue/num 节点,而且赋值为 n(或者直接给/queue 赋值 n),表示队列大小,以后每次有队列成员加入后,就判断下是否已经到达队列大小,决定是否能够开始执行了。这种用法的典型场景是,分布式环境中,一个大任务 Task A,须要在不少子任务完成(或条件就绪)状况下才能进行。这个时候,凡是其中一个子任务完成(就绪),那么就去 /taskList 下创建本身的临时时序节点(CreateMode.EPHEMERAL_SEQUENTIAL),当 /taskList 发现本身下面的子节点知足指定个数,就能够进行下一步按序进行处理了。
优缺点
优势
- 高可靠
- 应用普遍,比较成熟
- 支持监听事件
- API简单
缺点
- 对于每一个 watch 请求,zookeeper 都会打开一个新的 socket 链接,这样 zookeeper 就须要实时管理不少 socket 链接,比较复杂
总结
zookeeper是一款成熟的协调服务,应用普遍。