Skip to content

Introduction to Zookeeper

ZooKeeper 是一个基于 Google Chubby 论文实现的一款解决分布式数据一致性问题的开源实现,方便了依赖 ZooKeeper 的应用实现 数据发布 / 订阅、负载均衡、服务注册与发现、分布式协调、事件通知、集群管理、Leader 选举、 分布式锁和队列等功能。

基本概念

集群角色

一般的,在分布式系统中,构成集群的每一台机器都有自己的角色,最为典型的集群模式就是 Master / Slave 主备模式。在该模式中,我们把能够处理所有写操作的机器称为 Master 节点,并把所有通过异步复制方式获取最新数据、提供读服务的机器称为 Slave 节点。

zk-master-slave

而 ZooKeeper 中,则是引入了 领导者(Leader)、跟随者(Follower)、观察者(Observer) 三种角色 和 领导(Leading)、跟随(Following)、观察(Observing)、寻找(Looking) 等相应的状态。在 ZooKeeper 集群中的通过一种 Leader 选举的过程,来选定某个节点作为 Leader 节点,该节点为客户端提供读和写服务。而 Follower 和 Observer 节点,则都能提供读服务,唯一的区别在于,Observer 机器不参与 Leader 选举过程 和 写操作的”过半写成功”策略,Observer 只会被告知已经 commit 的 proposal。因此 Observer 可以在不影响写性能的情况下提升集群的读性能(详见下文 “性能优化 - 优化策略 - Observer 模式” 部分)

zk-leader-observer

会话

Session 指客户端会话。在 ZooKeeper 中,一个客户端会话是指 客户端和服务器之间的一个 TCP 长连接。客户端启动的时候,会与服务端建立一个 TCP 连接,客户端会话的生命周期,则是从第一次连接建立开始算起。通过这个连接,客户端能够通过心跳检测与服务器保持有效的会话,并向 ZooKeeper 服务器发送请求并接收响应,以及接收来自服务端的 Watch 事件通知。

Session 的 sessionTimeout 参数,用来控制一个客户端会话的超时时间。当服务器压力太大 或者是网络故障等各种原因导致客户端连接断开时,Client 会自动从 ZooKeeper 地址列表中逐一尝试重连(重试策略可使用 Curator 来实现)。只要在 sessionTimeout 规定的时间内能够重新连接上集群中任意一台服务器,那么之前创建的会话仍然有效。如果,在 sessionTimeout 时间外重连了,就会因为 Session 已经被清除了,而被告知 SESSION_EXPIRED,此时需要程序去恢复临时数据;还有一种 Session 重建后的在新节点上的数据,被之前节点上因网络延迟晚来的写请求所覆盖的情况,在 ZOOKEEPER-417 中被提出,并在该 JIRA 中新加入的 SessionMovedException,使得 用同一个 sessionld/sessionPasswd 重建 Session 的客户端能感知到,但是这个问题到 ZOOKEEPER-2219 仍然没有得到很好的解决。

zk-transition

数据模型

在 ZooKeeper 中,节点分为两类,第一类是指 构成集群的机器,称之为机器节点;第二类则是指 数据模型中的数据单元,称之为数据节点 ZNode。ZooKeeper 将所有数据存储在内存中,数据模型的结构类似于树(ZNode Tree),由斜杠(/)进行分割的路径,就是一个 ZNode,例如 /foo/path1。每个 ZNode 上都会保存自己的数据内容 和 一系列属性信息。

ZNode 可以分为持久节点(PERSISTENT)和临时节点(EPHEMERAL)两类。所谓持久节点是指一旦这个 ZNode 被创建了,除非主动进行移除操作,否则这个节点将一直保存在 ZooKeeper 上。而临时节点的生命周期,是与客户端会话绑定的,一旦客户端会话失效,那么这个客户端创建的所有临时节点都会被移除。在 HBase 中,集群则是通过 /hbase/rs/* 和 /hbase/master 两个临时节点,来监控 HRegionServer 进程的加入和宕机 和 HMaster 进程的 Active 状态。

另外,ZooKeeper 还有一种 顺序节点(SEQUENTIAL)。该节点被创建的时候,ZooKeeper 会自动在其子节点名上,加一个由父节点维护的、自增整数的后缀(上限:Integer.MAX_VALUE)。该节点的特性,还可以应用到 持久 / 临时节点 上,组合成 持久顺序节点(PERSISTENT_SEQUENTIAL) 和 临时顺序节点(EPHEMERAL_SEQUENTIAL)。

zk-znode

版本

ZooKeeper 的每个 ZNode 上都会存储数据,对应于每个 ZNode,ZooKeeper 都会为其维护一个叫做 Stat 的数据结构,Stat 中记录了这个 ZNode 的三个数据版本,分别是 version(当前 ZNode 数据内容的版本),cversion(当前 ZNode 子节点的版本)和 aversion(当前 ZNode 的 ACL 变更版本)。这里的版本起到了控制 ZooKeeper 操作原子性的作用(详见下文 “源码分析 - 落脚点 - ZooKeeper 乐观锁” 部分)

如果想要让写入数据的操作支持 CAS,则可以借助 Versionable#withVersion 方法,在 setData() 的同时指定当前数据的 verison。如果写入成功,则说明在当前数据写入的过程中,没有其他用户对该 ZNode 节点的内容进行过修改;否则,会抛出一个 KeeperException.BadVersionException,以此可以判断本次 CAS 写入是失败的。而这样做的好处就是,可以避免“并发局部更新 ZNode 节点内容”时,发生相互覆盖的问题。

Watcher

Watcher(事件监听器)是 ZooKeeper 提供的一种 发布/订阅的机制。ZooKeeper 允许用户在指定节点上注册一些 Watcher,并且在一些特定事件触发的时候,ZooKeeper 服务端会将事件通知给订阅的客户端。该机制是 ZooKeeper 实现分布式协调的重要特性。

zk-watcher

ACL

类似于 Unix 文件系统,ZooKeeper 采用 ACL(Access Control Lists)策略来进行权限控制。

CommandComment
CREATE [c]创建子节点的权限
READ [r]获取节点数据和子节点列表的权限
WRITE [w]更新节点数据的权限
DELETE [d]删除当前节点的权限
ADMIN [a]管理权限,可以设置当前节点的 permission
SchemeIDComment
worldanyoneZooKeeper 中对所有人有权限的结点就是属于 world:anyone
auth不需要 id通过 authentication 的 user 都有权限
digestusername:BASE64 (SHA1(password))需要先通过 username:password 形式的 authentication
ipid 为客户机的 IP 地址(或者 IP 地址段)ip:192.168.1.0/14,表示匹配前 14 个 bit 的 IP 段
super对应的 id 拥有超级权限(CRWDA)

常用命令

CommandComment
conf输出相关服务配置的详细信息
cons列出所有连接到服务器的客户端的完全的连接 / 会话的详细信息(包括“接受 / 发送”的包数量、会话 id 、操作延迟、最后的操作执行等等信息)
envi输出关于服务环境的详细信息 (区别于 conf 命令)
dump列出未经处理的会话和临时节点
stat查看哪个节点被选择作为 Follower 或者 Leader
ruok测试是否启动了该 Server,若回复 imok 表示已经启动
mntr输出一些运行时信息(latency / packets / alive_connections / outstanding_requests / server_state / znode + watch + ephemerals count …)
reqs列出未经处理的请求
wchs列出服务器 watch 的简要信息
wchc通过 session 列出服务器 watch 的详细信息(输出是一个与 watch 相关的会话的列表)
wchp通过路径列出服务器 watch 的详细信息(输出一个与 session 相关的路径)
srvr输出服务的所有信息(可以用来检查当前节点同步完毕集群数据,处于 Follower 状态)
srst重置服务器统计信息
kill关掉 Server

命令执行

echo <four-letter command> | nc IP 2181

常用配置

  • dataDir: ZooKeeper 保存服务器存储快照文件的目录,默认情况,ZooKeeper 将 写数据的日志文件也保存在这个目录里(default:/tmp/zookeeper)
  • dataLogDir: 用来存储服务器事务日志
  • clientPort: 客户端连接 ZooKeeper 服务器的端口,ZooKeeper 会监听这个端口,接受客户端的访问请求(default:2181)
  • tickTime(SS / CS): 用来指示 服务器之间或客户端与服务器之间维护心跳机制的 最小时间单元,Session 最小过期时间默认为两倍的 tickTime(default:2000ms)
  • initLimit(LF): 集群中的 Leader 节点和 Follower 节点之间初始连接时能容忍的最多心跳数(default:5 tickTime)
  • syncLimit(LF): 集群中的 Leader 节点和 Follower 节点之间请求和应答时能容忍的最多心跳数(default:2 tickTime)
  • minSessionTimeout & maxSessionTimeout: 默认分别是 2 x tickTime - 20 x tickTime,来用控制 客户端设置的 Session 超时时间。如果超出或者小于,将自动被服务端强制设置为 最大或者最小
  • maxClientCnxns: 控制单个客户端(以 IP 地址为唯一标识)创建连接数的上限(default:60),设置为 0 则不作限制

参考资料

  • https://zookeeper.apache.org
  • https://yuzhouwan.com/posts/31915
  • https://static.googleusercontent.com/media/research.google.com/zh-CN//archive/chubby-osdi06.pdf

Disclaimer
  1. License under CC BY-NC 4.0
  2. Copyright issue feedback me#imzye.me, replace # with @
  3. Not all the commands and scripts are tested in production environment, use at your own risk
  4. No privacy information is collected here
Try my iOS App