Zookeeper是雅虎开发的开源高性能分布式协调服务,封装复杂分布式一致性服务,提供简单易用接口。它使用内存存储数据,支持增删改查及权限控制,确保顺序一致性、原子性、单一视图、可靠性和实时性。部署支持单机和集群,核心角色包括Leader、Follower和Observer。通过节点和事务实现数据模型管理,支持会话和数据节点管理,并应用ACL进行权限控制。支持数据发布订阅、Master选举、分布式队列、分布式锁等应用场景。
基本介绍
Zookeeper是一个开放源代码的高性能的分布式协调服务。Zookeeper由雅虎创建,设计之初就是为了将复杂且容易出错的分布式一致性服务封装起来,构成一个高效可靠的原语集,并提供一些列简单易用的接口。
基本功能
Zookeeper提供类似于文件系统目录结构的数据存储模型(称为znode),不同于写磁盘的文件系统,数据存储在内存中,并且每一级目录可以存放自身的数据。同时zoopkeer提供对znode的增删改查,权限控制,并提供以下分布式一致特性保证:
顺序一致性
同一个客户端发起的事务请求,最终将会严格地按照其发起顺序被应用到ZooKeeper
原子性
所有事务请求的处理结果在整个集群中所有机器上的应用情况是一致的。要么整个集群都成功应用了某一个事务,要么都没有应用。
单一视图
无论客户端连接的是哪个ZooKeeper服务器,其看到的服务端数据模型都是一致的。即不会看到比之前的数据更旧。(客户端重连时候会发送看到的最新ZXID,被连上的服务端节点验证该zxid,落后则连接不合法)
可靠性
一旦服务端成功的应用了一个事务,并完成对客户端的响应,该事务的所引起的服务端状态变更将一直被保留,直到另一个事务对其进行变更。
实时性
ZooKeeper仅保证在一定的时间内,客户端最终一定能够从服务端上读取到最新的数据状态。
基本使用
环境要求
Java环境支持
操作系统:windows,mac,GNU/linux和Sun solaris都支持
单机部署
1. 下载安装包并解压
2. 在config文件夹下,创建配置文件zoo.cfg,内容如下
tickTime:基础时间单位,用于心跳检测,最小会话超时时间是2倍的tickTime.
dataDir:数据快照和事务日志文件
clientPort:客户端连接的接口
3. 启动zookeeper服务
zookeeper-3.4.11 ./bin/zkServer.sh start
4. 客户端连接到zookeeper服务
./bin/zkCli.sh
也可以使用telnet
集群部署
1. 下载安装包并解压
2. 在config文件夹下,创建配置文件zoo.cfg,内容如下
tickTime:基础时间单位(毫秒ms),用于心跳检测,最小会话超时时间是2倍的tickTime.
dataDir:数据快照和事务日志文件
clientPort:客户端连接的接口
initLimit:tickTime的5倍,作为follower服务器启动并完成从leader同步数据的时间
syncLimit:tickTime的2倍,作为leader服务器和follower服务器心跳检测的最大延时时间
server.1:1为serverId需要与配置中的myid文件中的数字对应,zoo1为host,第一个端口为用于Follower和leader服务器通信和数据同步使用;第二个端口用于Leader选举通信专用
3. 将zookpeer文件复制到各台机器(如果是单机,则需要指定不同端口)
4. 在各台机器中dataDir对应路径下创建myid文件,文件内容为serverId
5. 分别启动zookeeper服务
6. 客户端连接到zookeeper服务
./bin/zkCli.sh -server 127.0.0.1:2181,127.0.0.1:2182,127.0.0.1:2183
也可以使用telnet
zookeeper系统模型
核心概念
集群角色
Leader
为客户端提供读写服务,运行期间只存在一个leader。
事务请求的唯一调度和处理者,保证集群事务处理的顺序性。
集群内部各服务器的调度者。
Follower
提供读服务,遇到事务请求时转发给leader。
参与过半写成功策略和leader选举。
Observer
提供读服务,遇到事务请求时转发给leader。
不参与leader选举和过半写成功策略,主要用于提升集群读性能。
会话(Session)
Session指客户端会话,zookeeper中客户端和服务器建立TCP长连接,通过心跳检测,保持会话有效。即使连接断开,只要在sessionTimeout规定的时间内重新连上其它服务器,会话仍然有效。(从jconsole看只有和客户端连接的机器节点由连接信息)
数据节点(Znode)
构成集群的机器,我们称之为机器节点。数据模型中的数据单元称之为数据节点-Znode。数据模型是一棵树Znode Tree。由斜杠(/)分割路径例如/mynode/path1,每个节点保存自己的数据内容,一些列属性信息和子节点信息。
节点类型可以分为持久节点,临时节点和顺序节点三大类,生成以下4种节点类型。
持久节点
数据节点一经创建,一直保存,直到被删除。
持久节顺序点
与持久节点特性一直,额外特性为顺序性。ZooKeeper中,每个父节点都会为它的第一级子节点维护一份顺序,记录每个子节点的创建顺序。ZooKeeper自动为给定节点名加上数字后缀,上限为整型最大值。
临时节点
一旦客户端会话失效,临时节点会被自动清理掉。临时节点不能创建子节点,只能作为叶子节点。
临时顺序节点
与临时节点特性一致,增加了顺序性。
事务
是指能够改变ZooKeeper服务器状态的操作,也称为事务操作或更新操作,一般包括数据节点创建与删除,数据节点内容更新和客户端会话创建与失效等操作。对于每一个事务请求,ZooKeeper都会为其分配一个全局唯一的事务ID,用ZXID来表示。
版本
每个ZNode都维护一个Stat的数据结构,记录了ZNode的三个版本信息,
保证原子性操作。
dataVersion
数据版本号,每次数据内容变更,都会递增,与数据内容是否变化无关。
Cversion
子节点版本号,子节点
Aversion
ACL版本
Watcher
事件监听器,zookeeper允许用户在指定节点上注册一些watcher,在特定事件触发时,zooKeeper服务端会将时间通知到感兴趣的客户端上。
三个特性如下:
一次性
无论是服务端还是客户端,一个Wtcher一旦被触发,就会被移除,所以需要反复注册。可以减轻服务端压力。
客户端串行执行
客户端的Watcher回调过程是一个串行同步的过程,保证了顺序性,单也可能一个Watcher的回调逻辑导致阻塞了整个客户端的Watcher回调。
轻量
客户端注册watcher时,只使用了boolean类型进行了标记。
服务端通知只包含状态,事件类型和节点路径。
ACL
ZooKeeper采用ACL(Access Control Lists)策略来进行权限控制
CREATE
创建子节点的权限。
READ
获取节点数据和子节点列表的权限。
WRITE
更新节点数据的权限。
DELETE
删除子节点的权限。
ADMIN
设置节点ACL的权限。
应用场景与实现机制
数据发布与订阅
描述
即配置中心,发布者将数据发布到Zookeeper的一个或一系列节点,供订阅者进行数据订阅,进而达到动态获取数据的目的,实现配置信息的集中式管理和数据的动态更新。
机制
配置存储
将需要初始化的配置信息存储到ZooKeeper上,例如创建节点/app/config(持久节点),节点上键值对信息(作为节点数据)。
配置获取
集群中每台机器在启动chushihua 阶段,从ZooKeeper节点上读取配置信息,并在该节点上注册一个数据变更的Watcher监听,一旦节点数据变更,则所有订阅的客户端都能获取到数据变更通知。
配置变更
一旦需要变更配置时,直接对ZooKeeper上配置的节点的内容进行更新,ZooKeeper则会将数据变更通知发送给客户端,每个客户端收到通知后,就可以重新获取最新数据(需要一次数据获取)。
Master选举
描述
用于在分布式系统中选取master节点,master负责协调其他集群中的系统单元。
机制
在客户端启动后,马上在zookeeper创建一个临时节点/master_election/binding。就算由多个客户端同时请求,也只有一个客户端能够成功创建这个节点。
没有创建成功的客户端则在该节点上注册一个变更器Watcher。
一旦zookeeper发现mater挂了,就会通知其它客户端,收到通知的客户端就可以重新选举master(抢注该节点)。
分布式队列
描述
FIFO队列
先入先出的队列,即先到先执行。
Barrier屏障
规定一个队列的元素必须都集聚之后才能统一进行安排。类似jdk中的CountDownLatch。
机制
FIFO队列
FIFO队列类似于一个全写请求的共享锁模型。
所有客户端都会到/queue_fifo下创建一个临时节点/queue_fifo/192.168.0.1-000000001
1. 调用getChildren()获取/queue_fifo所有子节点
2. 确定自己节点序号所处的顺序
3. 如果不是最小子节点,则进入等待,并向比自己序号小的节点注册watcher
4. 接到监听后重复步骤1。
Barrier屏障
创建/queue_barrier节点,节点数据内容为数字n,表示需要等待的子节点数目。
客户端创建子节点/queue_barrier/192.168.0.1
1. 通过getData()获取/queue_barrier数据
2. 通过getChildren()获取/queue_barrier所有子节点,并对其子节点变更添加watcher
3. 统计子节点个数
4. 子节点个数小于10个,就进入等待
5. 接受watcher后重复步骤2。
分布式锁
描述
排他锁
被称为写锁,又称为独占锁,如果事务T1对数据对象O1加了排他锁,那么整个加锁期间,只允许事务T1对O1进行读取和更新操作,其他任何事务都不能再对这个数据对象进行任何类型的操作,直到T1释放了排他锁。
共享锁
共享锁,又称之为读锁,如果事务T1对数据对象O1加了共享锁,那么当前事务只能对O1进行读取操作,其他事务也只能对这个数据对象加共享锁,直到该对象的所有共享锁都被释放。
机制
排他锁
获取锁
在节点/exclusive_lock节点下创建临时子节点/exclusive_lock/lock。
创建成功的客户端的就获取了锁,没有获取到锁的客户端注册/exclusive_lock子节点变更的Watcher监听。
释放锁
获取锁的客户端机器宕机,ZooKeeper临时节点被移除
正常执行完业务后,客户端主动删除自己创建的临时节点。
共享锁
客户端在/shred_lock下创建临时顺序子节点[hostname-请求类型-],例如/shred_lock/192.168.0.1-R-000000001,如果是写请求则创建
/shred_lock/192.168.0.1-W-000000001。
1.创建完节点后,注册/shred_lock子节点的变更Watcher监听。
2.确定自己节点序号在所有子节点中的顺序。
3.对于读请求,如果没有比自己小的子节点,或者所有比自己小的子节点都是读请求,就表明获取了读锁,可以执行业务,否则则进入等待
对于写请求,如果自己不是最小的子节点,则进入等待。
4.接到通知后,重复步骤1
优化,只关注比自己小的那个节点是否存在(读请求,是关注比自己小的写请求,写请求时关注比自己序号小的最后一个节点)
Zookeeper中的请求处理
非事务请求
1. 会话检查
2. 获取数据节点(获取ACL信息)
3. ACL检查
4. 注册Watcher
5. 创建响应
6. 统计处理
7. 返回客户端
事务请求
1.请求转发(非leader)
2.会话检查
3.ACL检查
4.数据版本检查
5.事务处理(半数成功策略,二段提交)
6.统计处理
7.创建响应
8. 返回客户端
注:
本文独家发布自金蝶云社区
推荐阅读