ZooKeeper 是一个开源的分布式协调框架,是Apache Hadoop 的一个子项目,主要用来解决分布式集群中应用系统的一致性问题。Zookeeper 的设计目标是将那些复杂且容易出错的分布式一致性服务封装起来,构成一个高效可靠的原语集,并以一系列简单易用的接口提供给用户使用。
官方:Apache ZooKeeper
ZooKeeper本质上是一个分布式的小文件存储系统(Zookeeper=文件系统+监听机制)。提供基于类似于文件系统的目录树方式的数据存储,并且可以对树中的节点进行有效管理,从而用来维护和监控存储的数据的状态变化。通过监控这些数据状态的变化,从而可以达到基于数据的集群管理、统一命名服务、分布式配置管理、分布式消息队列、分布式锁、分布式协调等功能。
Zookeeper从设计模式角度来理解:是一个基于观察者模式设计的分布式服务管理框架,它负责存储和管理大家都关心的数据,然后接受观察者的注册,一旦这些数据的状态发生变化,Zookeeper 就将负责通知已经在Zookeeper上注册的那些观察者做出相应的反应。
下载地址:Apache ZooKeeper
运行环境:jdk8
解压安装包后进入conf目录,复制zoo_sample.cfg,修改为zoo.cfg
cp zoo_sample.cfg zoo.cfg
修改 zoo.cfg 配置文件,将 dataDir=/tmp/zookeeper 修改为指定的data目录
zoo.cfg中参数含义:
# 可以通过 bin/zkServer.sh 来查看都支持哪些参数
# 默认加载配置路径conf/zoo.cfg
bin/zkServer.sh start conf/zoo.cfg
# 查看zookeeper状态
bin/zkServer.sh status
bin/zkCli.sh
# 连接远程的zookeeper server
bin/zkCli.sh -server ip:port
输入命令 help 查看zookeeper支持的所有命令:
ZooKeeper: Because Coordinating Distributed Systems is a Zoo
命令基本语法 |
功能描述 |
help |
显示所有操作命令 |
ls [-s] [-w] [-R] path |
使用 ls 命令来查看当前 znode 的子节点 [可监听] -w: 监听子节点变化 -s: 节点状态信息(时间戳、版本号、数据大小等) -R: 表示递归的获取 |
create [-s] [-e] [-c] [-t ttl] path [data] [acl] |
创建节点 -s : 创建有序节点。 -e : 创建临时节点。 -c : 创建一个容器节点。 t ttl] : 创建一个TTL节点, -t 时间(单位毫秒)。 data:节点的数据,可选,如果不使用时,节点数据就为null。 acl:访问控制 |
get [-s] [-w] path |
获取节点数据信息 -s: 节点状态信息(时间戳、版本号、数据大小等) -w: 监听节点变化 |
set [-s] [-v version] path data |
设置节点数据 -s:表示节点为顺序节点 -v: 指定版本号 |
getAcl [-s] path |
获取节点的访问控制信息 -s: 节点状态信息(时间戳、版本号、数据大小等) |
setAcl [-s] [-v version] [-R] path acl |
设置节点的访问控制列表 -s:节点状态信息(时间戳、版本号、数据大小等) -v:指定版本号 -R:递归的设置 |
stat [-w] path |
查看节点状态信息 |
delete [-v version] path |
删除某一节点,只能删除无子节点的节点。 -v: 表示节点版本号 |
deleteall path |
递归的删除某一节点及其子节点 |
setquota -n|-b val path |
对节点增加限制 n:表示子节点的最大个数 b:数据值的最大长度,-1表示无限制 |
ZooKeeper 数据模型的结构与 Unix 文件系统很类似,整体上可以看作是一棵树,每个节点称做一个 ZNode。
ZooKeeper的数据模型是层次模型,层次模型常见于文件系统。层次模型和key-value模型是两种主流的数据模型。ZooKeeper使用文件系统模型主要基于以下两点考虑:
ZooKeeper的层次模型称作Data Tree,Data Tree的每个节点叫作Znode。不同于文件系统,每个节点都可以保存数据,每一个 ZNode 默认能够存储 1MB 的数据,每个 ZNode 都可以通过其路径唯一标识,每个节点都有一个版本(version),版本从0开始计数。
public class DataTree {
private final ConcurrentHashMap nodes =
new ConcurrentHashMap();
private final WatchManager dataWatches = new WatchManager();
private final WatchManager childWatches = new WatchManager();
}
public class DataNode implements Record {
byte data[];
Long acl;
public StatPersisted stat;
private Set children = null;
}
一个znode可以使持久性的,也可以是临时性的:
1. 持久节点(PERSISTENT): 这样的znode在创建之后即使发生ZooKeeper集群宕机或者client宕机也不会丢失。
2. 临时节点(EPHEMERAL ): client宕机或者client在指定的timeout时间内没有给ZooKeeper集群发消息,这样的znode就会消失。
如果上面两种znode具备顺序性,又有以下两种znode :
3. 持久顺序节点(PERSISTENT_SEQUENTIAL): znode除了具备持久性znode的特点之外,znode的名字具备顺序性。
4. 临时顺序节点(EPHEMERAL_SEQUENTIAL): znode除了具备临时性znode的特点之外,zorde的名字具备顺序性。
zookeeper主要用到的是以上4种节点。
5. Container节点 (3.5.3版本新增):Container容器节点,当容器中没有任何子节点,该容器节点会被zk定期删除(定时任务默认60s 检查一次)。 和持久节点的区别是 ZK 服务端启动后,会有一个单独的线程去扫描,所有的容器节点,当发现容器节点的子节点数量为 0 时,会自动删除该节点。可以用于 leader 或者锁的场景中。
6. TTL节点: 带过期时间节点,默认禁用,需要在zoo.cfg中添加 extendedTypesEnabled=true 开启。 注意:ttl不能用于临时节点
#创建持久节点
create /servers xxx
#创建临时节点
create -e /servers/host xxx
#创建临时有序节点
create -e -s /servers/host xxx
#创建容器节点
create -c /container xxx
# 创建ttl节点
create -t 10 /ttl
对于zk来说,每次的变化都会产生一个唯一的事务id,zxid(ZooKeeper Transaction Id),通过zxid,可以确定更新操作的先后顺序。例如,如果zxid1小于zxid2,说明zxid1操作先于zxid2发生,zxid对于整个zk都是唯一的,即使操作的是不同的znode。
在client和server通信之前,首先需要建立连接,该连接称为session。连接建立后,如果发生连接超时、授权失败,或者显式关闭连接,连接便处于closed状态, 此时session结束。
watcher的过程:
注意:Zookeeper中的watch机制,必须客户端先去服务端注册监听,这样事件发送才会触发监听,通知给客户端。
支持的事件类型:
特性 |
说明 |
一次性触发 |
watcher是一次性的,一旦被触发就会移除,再次使用时需要重新注册 |
客户端顺序回调 |
watcher回调是顺序串行执行的,只有回调后客户端才能看到最新的数据状态。一个watcher回调逻辑不应该太多,以免影响别的watcher执行 |
轻量级 |
WatchEvent是最小的通信单位,结构上只包含通知状态、事件类型和节点路径,并不会告诉数据节点变化前后的具体内容 |
时效性 |
watcher只有在当前session彻底失效时才会无效,若在session有效期内快速重连成功,则watcher依然存在,仍可接收到通知; |
#监听节点数据的变化
get -w path
stat -w path
#监听子节点增减的变化
ls -w path
设计一个master-worker的组成员管理系统,要求系统中只能有一个master , master能实时获取系统中worker的情况。
保证组里面只有一个master的设计思路
#master1
create -e /master "m1:2223"
#master2
create -e /master "m2:2223" # /master已经存在,创建失败
Node already exists: /master
#监听/master节点
stat -w /master
#当master2收到/master节点删除通知后可以再次发起创建节点操作
create -e /master "m2:2223"
master-slave选举也可以用这种方式
master监控worker状态的设计思路
#master服务
create /workers
#让master服务监控/workers下的子节点
ls -w /workers
#worker1
create -e /workers/w1 "w1:2224" #创建子节点,master服务会收到子节点变化通知
#master服务
ls -w /workers
#worker2
create -e /workers/w2 "w2:2224" #创建子节点,master服务会收到子节点变化通知
#master服务
ls -w /workers
#worker2
quit #worker2退出,master服务会收到子节点变化通知
设想用2 /c实现一个counter,使用set命令来实现自增1操作。条件更新场景∶
1. 客户端1把/c更新到版本1,实现/c的自增1。
2. 客户端2把/c更新到版本2,实现/c的自增1。
3. 客户端1不知道/c已经被客户端⒉更新过了,还用过时的版本1是去更新/c,更新失败。如果客户端1使用的是无条件更新,/c就会更新为2,没有实现自增1。
使用条件更新可以避免出现客户端基于过期的数据进行数据更新的操作。