ZooKeeper详解(五):ZooKeeper系统模型

六、ZooKeeper系统模型

1、数据模型

1)、树

ZNode是ZooKeeper中数据的最小单元,每个ZNode上都可以保存数据,同时还可以挂载子节点,因此构成了一个层次化的命名空间,称之为树
ZooKeeper详解(五):ZooKeeper系统模型_第1张图片
在ZooKeeper中,每一个数据节点都被称为一个ZNode,所有ZNode按层次化结构进行组织,形成一棵树。ZNode的节点路径标识方式和Unix文件系统路径非常相似,都是由一系列使用/进行分割的路径表示,开发人员可以向这个节点写入数据,也可以在节点下面创建子节点

2)、事务ID

在ZooKeeper中,事务是指能够改变ZooKeeper服务器状态的操作,称之为事务操作或更新操作,一般包括数据节点创建与删除、数据节点内容更新和客户端会话创建与失效等操作。对于每一个事务请求,ZooKeeper都会为其分配一个全局唯一的事务ID,用ZXID来表示,通常是一个64位的数字。每一个ZXID对应一次更新操作,从这些ZXID中可以间接地识别出ZooKeeper处理这些更新操作请求的全局顺序

2、节点特性

1)、节点类型

在ZooKeeper中,每个数据节点都是有生命周期的,其生命周期的长短取决于数据节点的节点类型。在ZooKeeper中,节点类型可以分为持久节点、临时节点和顺序节点三大类,具体在节点创建过程中,通过组合使用,可以生成以下四种组合型节点类型:

1)持久节点

持久节点是指该数据节点被创建后,就会一直存在于ZooKeeper服务器上,直到有删除操作来主动请求清除这个节点

2)持久顺序节点

持久顺序节点的基本特性和持久节点是一致的,额外的特性表现在顺序性上。在ZooKeeper中,每个父节点都会为它的第一级子节点维护一份顺序,用于记录下每个子节点创建的先后顺序。基于这个顺序特性,在创建子节点的时候,可以设置这个标记,那么在创建节点过程中,ZooKeeper会自动为给定节点名加上一个数字后缀,作为一个新的、完整的节点名。这个数字后缀的上限是整型的最大值

3)临时节点

临时节点的生命周期和客户端会话绑定在一起,如果客户端会话失效,那么这个节点就会被自动清理掉。注意,这里提到的是客户端会话失效而不是TCP连接断开。另外,ZooKeeper规定了不能基于临时节点来创建子节点,即临时节点只能作为叶子节点

4)临时顺序节点

临时顺序节点的基本特性和临时节点也是一致的,同样是在临时节点的基础上,添加了顺序的特性

2)、状态信息

每个数据节点除了存储了数据内容之外,还存储了数据节点本身的一些状态信息。使用get命令来获取一个数据节点的内容

[zk: localhost:2181(CONNECTED) 0] get /zk01
hello
cZxid = 0x5
ctime = Sun Aug 18 09:46:10 CST 2019
mZxid = 0x5
mtime = Sun Aug 18 09:46:10 CST 2019
pZxid = 0x5
cversion = 0
dataVersion = 0
aclVersion = 0
ephemeralOwner = 0x0
dataLength = 5
numChildren = 0

返回的结果中,第一行是当前数据节点的数据内容,从第二行开始就是节点的状态信息了

状态属性 说明
cZxid 表示该节点被创建时的事务ID
ctime 表示该节点被创建的时间
mZxid 表示该节点最后一次被更新时的事务ID
mtime 表示该节点最后一次被更新的时间
pZxid 表示该节点的子节点列表最后一次被修改时的事务ID。只有子节点列表变更了才会变更pZxid,子节点内容变更不会影响pZxid
cversion 子节点的版本号
dataVersion 数据节点的版本号
aclVersion 节点的ACL版本号
ephemeralOwner 创建该临时节点的会话的sessionID。如果该节点是持久节点,那么这个属性值为0
dataLength 数据内容的长度
numChildren 当前节点的子节点个数

3、版本——保证分布式数据原子性操作

ZooKeeper中为数据节点引入了版本的概念,每个数据节点都具有三种类型的版本信息,对数据节点的任何更新操作都会引起版本号的变化

版本类型 说明
dataVersion 数据节点的版本号
cversion 子节点的版本号
aclVersion 节点的ACL版本号

在ZooKeeper服务器的PrepRequestProcessor处理类中,在处理每一个数据更新请求时,会进行如下所示的版本检查

                version = setDataRequest.getVersion();
                int currentVersion = nodeRecord.stat.getVersion();
                if (version != -1 && version != currentVersion) {
                    throw new KeeperException.BadVersionException(path);
                }
                version = currentVersion + 1;

在进行一次setDataRequest请求处理时,首先进行了版本检查:ZooKeeper会从setDataRequest请求中获取到当前请求的版本version,同时从数据记录nodeRecord中获取当前服务器上该数据的最新版本currentVersion。如果version是-1,那么说明客户端并不要求使用乐观锁,可以忽略版本比对;如果version不是-1,那么就比对version和currentVersion,如果两个版本不匹配,那么将会抛出BadVersionException异常

4、Watcher——数据变更的通知

ZooKeeper允许客户端向服务器端注册一个Watcher监听,当服务端的一些指定事件触发了这个Watcher,那么就会向指定客户端发送一个事件通知来实现分布式的通知功能
ZooKeeper详解(五):ZooKeeper系统模型_第2张图片
ZooKeeper的Watcher机制主要包括客户端线程、客户端WatcherManager和ZooKeeper服务器三部分。客户端在向ZooKeeper服务器注册Watcher的同时,会将Watcher对象存储在客户端的WatcherManager中。当ZooKeeper服务端触发Watcher事件后,会向客户端发送通知,客户端线程从WatcherManager中取出对应的Watcher对象来执行回调逻辑

ZooKeeper的Watcher具有以下几个特性:

1)一次性

无论是服务端还是客户端,一旦一个Watcher被触发,ZooKeeper都会将其从相应的存储中移除

2)客户端串行执行

客户端Watcher回调的过程是一个串行同步的过程,保证了顺序

3)轻量

WatchedEvent是ZooKeeper整个Watcher机制的最小通知单元,这个数据结构只包含三个部分内容:通知状态、事件类型和节点路径。Watcher通知非常简单,只会告诉客户端发生了事件,而不会说明事件的具体内容。例如针对NodeDataChanged事件,ZooKeeper的Watcher只会通知客户端指定数据节点的数据内容发生了变更,而对于原始数据以及变更后的新数据都无法从这个事件中直接获取到,而是需要客户端主动重新去获取数据

5、ACL——保障数据的安全

可以从三个方面来理解ZooKeeper的ACL机制:权限模式(Scheme)、授权对象(ID)和权限(Permission),通常使用scheme:id:permission来标识一个有效的ACL信息

1)、ACL介绍

1)权限模式:Scheme

权限模式用来确定权限验证过程中使用的校验策略,在ZooKeeper中,开发人员使用最多的就是以下四种权限模式:

IP

IP模式通过IP地址粒度来进行权限控制。例如配置了ip:192.168.0.110,即表示权限控制都是针对这个IP地址的。同时,IP模式也支持按照网段的方式进行配置,例如ip:192.168.0.1/24表示针对192.168.0.*这个IP段进行权限控制

Digest

Digest是最常用的权限控制模式,类似于username:password形成的权限标识来进行权限配置

当通过username:password形式配置了权限标识后,ZooKeeper会对其先后进行两次编码吃力,分别是SHA-1算法加密和BASE64编码,是通过DigestAuthenticationProvider的generateDigest方法实现的:

    static public String generateDigest(String idPassword)
            throws NoSuchAlgorithmException {
        String parts[] = idPassword.split(":", 2);
        byte digest[] = MessageDigest.getInstance("SHA1").digest(
                idPassword.getBytes());
        return parts[0] + ":" + base64Encode(digest);
    }

World

World是一种最开放的权限控制模式,这种权限控制方式几乎没有任何作用,数据节点的访问权限对所有用户开放,即所有用户都可以在不进行任何权限校验的情况下操作ZooKeeper上的数据。World模式也可以看作是一种特殊的Digest模式,它只有一个权限标识,即world:anyone

Super

Super模式是一种特殊的Digest模式,在Super模式下,超级用户可以对任意ZooKeeper上的数据节点进行任何操作

2)授权对象:ID

授权对象指的是权限赋予的用户或一个指定实体,在不同的权限模式下,授权对象是不同的

权限模式 授权对象
IP 通常是一个IP地址或者IP段
Digest 自定义,通常是username:password
World 只有一个ID:anyone
Super 与Diges模式一致

3)权限:Permission

权限就是指那些通过权限检查后可以被允许执行的操作。在ZooKeeper中,所有对数据的操作权限分为以下五大类:

  • CREATE(C):数据节点的创建权限,允许授权对象在该数据节点下创建子节点
  • DELETE(D):子节点的删除权限,允许授权对象删除该数据节点的子节点
  • READ(R):数据节点的读取权限,允许授权对象访问该数据节点并读取其数据内容或子节点列表等
  • WRITE(W):数据节点的更新权限,允许授权对象对该数据节点进行更新操作
  • ADMIN(A):数据节点的管理权限,允许授权对象对该数据节点进行ACL相关的设置操作

2)、ACL管理

1)设置ACL

通过zkCli脚本登录ZooKeeper服务器后,可以通过两种方式进行ACL的设置。一种是在数据节点创建的同时进行ACL权限的设置,命令格式如下:

create [-s] [-e] path data acl
public class DigestAuthenticationProviderUsage {
    public static void main(String[] args) throws NoSuchAlgorithmException {
        System.out.println(DigestAuthenticationProvider.generateDigest("hxt:123456"));
    }
}

运行结果:

hxt:0BQvc3KJ71HY7VGstKgNJdOjWnM=
[zk: localhost:2181(CONNECTED) 0] create -e /zk init digest:hxt:0BQvc3KJ71HY7VGstKgNJdOjWnM=:cdrwa
Created /zk
[zk: localhost:2181(CONNECTED) 1] getAcl /zk
'digest,'hxt:0BQvc3KJ71HY7VGstKgNJdOjWnM=
: cdrwa

另一种方式是使用setAcl命令单独对已经存在的数据节点进行ACL设置:

setAcl path acl
[zk: localhost:2181(CONNECTED) 0] create -e /zk init  
Created /zk
[zk: localhost:2181(CONNECTED) 1] setAcl /zk digest:hxt:0BQvc3KJ71HY7VGstKgNJdOjWnM=:cdrwa
cZxid = 0x100000022
ctime = Sun Aug 18 14:24:47 CST 2019
mZxid = 0x100000022
mtime = Sun Aug 18 14:24:47 CST 2019
pZxid = 0x100000022
cversion = 0
dataVersion = 0
aclVersion = 1
ephemeralOwner = 0x10000e04d370005
dataLength = 4
numChildren = 0
[zk: localhost:2181(CONNECTED) 2] getAcl /zk
'digest,'hxt:0BQvc3KJ71HY7VGstKgNJdOjWnM=
: cdrwa

通过zkCli脚本开启一个新的会话:

[zk: localhost:2181(CONNECTED) 0] get /zk
Authentication is not valid : /zk
[zk: localhost:2181(CONNECTED) 1] addauth digest hxt:123456
[zk: localhost:2181(CONNECTED) 2] get /zk                  
init
cZxid = 0x10000002f
ctime = Sun Aug 18 14:34:49 CST 2019
mZxid = 0x10000002f
mtime = Sun Aug 18 14:34:49 CST 2019
pZxid = 0x10000002f
cversion = 0
dataVersion = 0
aclVersion = 0
ephemeralOwner = 0x10000e04d37000a
dataLength = 4
numChildren = 0

2)Super模式的用法

使用超级管理员权限,首先需要在ZooKeeper服务器上开启super模式,修改zkServer.sh脚本文件,找到如下一行:

nohup "$JAVA" "-Dzookeeper.log.dir=${ZOO_LOG_DIR}" "-Dzookeeper.root.logger=${ZOO_LOG4J_PROP}"

添加上超级管理员admin:123456

"-Dzookeeper.DigestAuthenticationProvider.superDigest=admin:0uek/hZ/V9fgiM35b0Z2226acMQ="

设置一个数据节点的ACL权限,用户名为hxt,密码为123456

[zk: localhost:2181(CONNECTED) 0] create -e /zk init digest:hxt:0BQvc3KJ71HY7VGstKgNJdOjWnM=:cdrwa
Created /zk
[zk: localhost:2181(CONNECTED) 1] getAcl /zk
'digest,'hxt:0BQvc3KJ71HY7VGstKgNJdOjWnM=
: cdrwa

通过zkCli脚本开启一个新的会话并使用超级管理员访问数据节点/zk

[zk: localhost:2181(CONNECTED) 0] get /zk
Authentication is not valid : /zk
[zk: localhost:2181(CONNECTED) 1] addauth digest admin:123456
[zk: localhost:2181(CONNECTED) 2] get /zk                    
init
cZxid = 0x500000006
ctime = Sun Aug 18 15:20:54 CST 2019
mZxid = 0x500000006
mtime = Sun Aug 18 15:20:54 CST 2019
pZxid = 0x500000006
cversion = 0
dataVersion = 0
aclVersion = 0
ephemeralOwner = 0x10001385c010001
dataLength = 4
numChildren = 0

你可能感兴趣的:(ZooKeeper)