重要概念
上一篇博客介绍了如何使用Shell
脚本搭建单机版的ZooKeeper
:
这篇博客,博主打算介绍ZooKeeper
的重要概念和提供的客户端命令。
ZooKeeper
的重要概念总结如下:
- 会话(
Session
):Session
指的是ZooKeeper
客户端与服务端的会话。客户端启动时,会与服务端建立一个TCP
长连接,客户端需要通过心跳与服务端保持会话,也可以向服务端发送请求并接受响应,并且还可以接收来自服务端的事件通知(Watcher
)。 - 节点(
Znode
):ZooKeeper
的数据模型是一棵树(Znode Tree
,由斜杠/
分割,类似Linux
的文件系统,Znode
就类似Linux
的文件)。Znode
上可以保存数据,同时还会保存一系列状态信息。 - 事件监听器(
Watcher
):Watcher
是ZooKeeper
很重要的特性。ZooKeeper
允许用户在指定Znode
注册Watcher
,当特定事件触发时,ZooKeeper
服务端会将事件通知到注册过该事件Watcher
的客户端,该机制是ZooKeeper
实现分布式协调服务的重要特性。 - 访问控制列表(
ACL
):ZooKeeper
采用ACL
(Access Control Lists
)策略来进行权限控制,类似于Linux
文件系统的权限控制。
接下来,博主将通过使用ZooKeeper
提供的客户端命令来详细介绍这些概念。
Session
在上一篇博客中已经介绍了如何使用ZooKeeper
提供的zkCli.sh
脚本连接服务端(默认是2181
端口,博主修改了配置文件):
./zkCli.sh -timeout 5000 -server 127.0.0.1:9000
session id
(全局唯一)和session timeout
(单位毫秒) :
session id = 0x100000899390000, negotiated timeout = 5000
在ZooKeeper
中,客户端和服务端建立连接后,Session
随之建立,生成一个全局唯一的会话ID
(session id
)。服务端和客户端之间维持的是一个长连接,在session timeout
时间内,服务端会确定客户端是否正常连接(客户端会定时向服务端发送心跳heart beat
)。这个session timeout
并不是客户端可以随意设置的,客户端会把这个session timeout
发送给服务端,服务端会返回一个它可以接受的session timeout
给客户端(tickTime
的[2, 20]
倍是服务端可以接受的session timeout
范围,tickTime
是ZooKeeper
中最小的时间单位长度 ,默认2000ms
,在上一篇博客中有介绍,因此服务端可以接受的session timeout
范围默认是[4s, 40s]
)。
现在来测试一下:
./zkCli.sh -timeout 3999 -server 127.0.0.1:9000
很显然session timeout
最短默认是4s
。
./zkCli.sh -timeout 40001 -server 127.0.0.1:9000
而session timeout
最长默认为40s
。
./zkCli.sh -timeout 25678 -server 127.0.0.1:9000
在[4s, 40s]
这个区间的session timeout
都是服务端可以接受的(默认情况)。
默认有三个持久Znode
(/zookeeper
、/zookeeper/config
和/zookeeper/quota
)。
zkCli.sh
脚本连接服务端还可以指定相对路径(之后在客户端执行的命令都是基于该相对路径):
./zkCli.sh -timeout 5000 -server 127.0.0.1:9000/zookeeper
查看ZooKeeper
服务端给客户端提供了哪些命令(help
,其实服务端并没有提供help
命令,当客户端执行的命令在服务端不存在时,服务端会返回提供的所有命令):
Session
状态转移如下图所示:
Session
从NOT_CONNECTED
状态开始,随着ZooKeeper
客户端初始化,转移到CONNECTING
状态(方向1
)。 正常情况下,客户端会与服务端连接成功,并且转移到CONNECTED
状态(方向2
)。当客户端失去了与服务端的连接或者不能感知到服务端的存在,它会转移回CONNECTING
(方向3
),并且尝试寻找另一个ZooKeeper
服务端。如果它能找到另一个服务端或者重新连接到之前的服务端,并确认了这个Session
仍然有效(没有超时),它会转移回CONNECTED
状态(方向2
)。否则,它会定义这个Session
失效,并转移到CLOSED
(方向4
)。客户端也可以主动关闭Session
。(方向4
和5
)
Znode
ZooKeeper
的数据模型是一棵树,由斜杠/
分割,类似Linux
的文件系统,如下图所示(图来自ZooKeeper
官网):
Znode
有如下类型:
- 持久、临时:持久是默认的
Znode
类型,临时Znode
相较于持久Znode
来说就是它会随着客户端会话结束而被删除,通常可以用在一些特定的场景,如分布式锁释放、健康检查等。 - 持久顺序、临时顺序:在上面两种
Znode
类型的基础上,ZooKeeper
会自动在这两种Znode
类型的节点名后加一个数字后缀(保证唯一),这数字后缀的应用场景可以实现诸如分布式队列,分布式公平锁等。 - 容器:容器
Znode
类型是3.5
以后新增的节点类型,容器Znode
和持久Znode
类似,但是区别是服务端启动后,会有一个单独的线程去扫描所有的容器Znode
,当发现容器Znode
的子节点数量为0
时,会自动删除该容器Znode
(删除时机,留到以后分析ZooKeeper
源码时再进行介绍)。 - TTL、顺序TTL:这两种
Znode
类型重点是TTL
(time to live
,存活时间,单位为秒) ,当该节点没有子节点并且超过了指定的TTL
时间后就会被自动删除,和容器Znode
类似,只是容器Znode
没有超时时间,使用TTL Znode
需要配置extendedTypesEnabled=true
,不然创建TTL Znode
时会收到Unimplemented
的报错。
每个Znode
除了可以存储数据外,其本身还存储了数据节点相关的一些状态信息。Znode
的状态信息如下表所示:
状态信息 | 描述 |
---|---|
cZxid | 该节点被创建时的事务id ,ZooKeeper 中的每个改变都会产生一个全局唯一的zxid ,通过它可确定更新操作的先后顺序 |
ctime | 该节点被创建的时间 |
mZxid | 该节点最后一次更新的事务id |
mtime | 该节点最后一次更新的时间 |
pZxid | 该节点的子节点列表最后一次修改的事务id ,只有子节点列表变更才会更新pZxid ,子节点内容变更不会更新 |
cversion | 子节点版本号,该节点的子节点变化时值就会增加1 |
dataVersion | 节点数据版本号,节点创建时为0 ,每更新一次节点数据(不管内容有无变化)该版本号的值增加1 |
aclVersion | 节点的ACL 版本号 |
ephemeralOwner | 创建该临时Znode 的session id ,如果是持久Znode ,则ephemeralOwner 为0 |
dataLength | 节点数据的长度 |
numChildren | 当前节点的子节点个数 |
使用stat
命令可以查看Znode
的状态信息(如下图所示,根Znode
的状态信息):
Watcher
Watcher
是ZooKeeper
中非常重要的特性, ZooKeeper
上创建的节点,可以对这些节点绑定监听事件,比如可以监听节点数据变更、节点删除、子节点状态变更等事件,通过这个事件机制,可以基于ZooKeeper
实现分布式锁、集群管理等功能。
Watcher
特性:比如当节点数据发生变化的时候,ZooKeeper
会产生一个Watcher
事件,并且会发送到客户端,客户端收到监听的节点事件后,就可以进行相应的业务处理了。ZooKeeper
的Watcher
机制,可以分为三个过程:客户端注册Watcher
、服务端处理Watcher
和客户端回调。
ACL
ACL
权限控制,主要包括3
个方面: 授权策略(Scheme
)、授权对象(ID
)以及授权权限(Permission
)。ZooKeeper
的权限控制是基于每个Znode
的,需要对每个节点设置权限,每个Znode
支持设置多种权限控制方案和多个权限,子节点不会继承父节点的权限,客户端无权访问某节点,但可能可以访问它的子节点。
授权策略(Scheme
):
world
:开放模式,world
表示任意客户端都可以访问(默认设置)。ip
:限定客户端IP
防问。auth
:只有在会话中通过了认证才可以访问(通过addauth
命令)。digest
:与auth
类似,区别在于auth
用明文密码,而digest
用SHA1
+base64
加密后的密码(通过addauth
命令,实际场景中digest
更常见)。
授权对象(ID
)就是指定的授权策略(Scheme
)的内容,比如world:anyone
中的anyone
、ip:192.168.1.189
中的192.168.1.189
、auth:username:password
中的username:password
(明文密码)、digest:username:password_digest
中的username:password_digest
(用SHA1
+base64
加密后的密码)。
授权权限(Permission
):
权限位 | 权限 | 描述 |
---|---|---|
c | CREATE | 可以创建子节点 |
d | DELETE | 可以删除子节点(仅直接子节点) |
r | READ | 可以读取节点数据及显示子节点列表 |
w | WRITE | 可以设置节点数据 |
a | ADMIN | 可以设置节点访问控制列表权限 |
到这里就结束了,如果博主有说错的地方或者大家有不同的见解,欢迎大家评论补充。