【ZooKeeper】ZooKeeper 系统模型

1 数据模型

ZooKeeper 的视图结构和标准的 Unix 文件系统非常类似,但没有引入传统文件系统中目 录和文件等相关概念,而是使用了其特有的“数据节点”概念,我们称之为 ZNode o ZNode 是 ZooKeeper 中数据的最小单元,每个 ZNode 上都可以保存数据,同时还可以挂载子节 点,因此构成了一个层次化的命名空间,我们称之为树。


首先我们来看下图所示的 ZooKeeper 数据节点示意图,从而对 ZooKeeper 上的数据节 点有一个大体上的认识。在 ZooKeeper 中,每一-个数据节点都被称为一个 ZNode, 所有 ZNode 按层次化构进行组织,形成一棵树。 ZNode 的节点路径标识方式和 Unix 文件 系统路径非常相似,都是由一系列使用斜杠(/)进行分割的径表示,开发人员可以向 这个节点中写入数据,也可以在节点下面创建子节点。
在这里插入图片描述

事务 ID
在《事务处理:概念与技术》一书中提到,事务是对物理和抽象的应用状态上的操作集 合。在现在的计算机科学中,狭义上的事务通常指的是数据库事务,一般包含了一系列 对数据库有序的读写操作,这些数据库事务具有所谓的 ACID 特性,即原子性 (Atomic) 、 一致性 (Consistency) 、隔离性(Isolation) 和持久性 (Durability) 。

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

2 节点特性

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

持久节点 (PERSISTENT)

  • 持久节点是 ZooKeeper 中最常见的一种节点类型。所谓持久节点,是指该数据节点被创 建后,就会一直存在于 ZooKeeper 服务器上,直到有删除操作来主动清除这个节点。

持久顺序节点 (PERSISTENT_SEQUENTIAL)

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

临时节点 (EPHEMERAL)

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

临时顺序节点 ( EPHEMERAL_SEQUENTIAL)

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

状态信息
我们提到可以针对 ZooKeeper 上的数据节点进行数据的写入和子节点的 创建。事实上,每个数据节点除了存储了数据内容之外,还存储了数据节点本身的一些 状态信息。使用 get 命令来获取一个数据节点的内容, 如图所示。
在这里插入图片描述
从返回结果中,我们可以看到,第一行是当前数据节点的数据内容,从第 二行开始就是节点的状态信息了,这其实就是数据节点的 Stat 对象的格式化输出
在这里插入图片描述

在这里插入图片描述

3 版本------证分布式数据原子性操作

ZooKeeper 中为数据节点引入了版本的概念,每个数据节点都具有三种类型的版本信息, 对数据节点的任何更新操作都会引起版本号的变化
在这里插入图片描述
ZooKeeper 中的版本概念和传统意义上的软件版本有很大的区别,它表示的是对数据节 点的数据内容、子节点列表,或是节点 ACL 信息的修改次数,我们以其中的 version 这种版本类型为例来说明。在一个数据节点 /zk-book 被创建完毕之后,节点的 version 值是 0, 表示的含义是“当前节点自从创建之后,被更新过 0 次”。如果现在对该节点的 数据内容进行更新操作,那么随后, version的值就会变成 1 。同时需要注意的是,在 上文中提到的关于 version 的说明,其表示的是对数据节点数据内容的变更次数,强 调的是变更次数,因此即使前后两次变更并没有使得数据内容的值发生变化, version 的值依然会变更。

事实上,在 ZooKeeper 中, version 属性正是用来实现乐观锁机制中的“写入校验”的。在ZooKeeper 服务器的PrepRequestProcessor 处理器类中, 在处理每一个数据更新 (setDataRequest)请求时,会进行版本检查。

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 0 如果 version 为 “-1”, 那么说明客户端并不要求使用乐观锁,可以忽略版本比对,如果 version 不是“一 1”, 那么就比对 version和 currentversion, 如果两个版本不匹配, 那么将会抛出 BadVersionException 异常。

4 ACL------ 保障数据的安全

从前面的介绍中,我们已经了解到, ZooKeeper 作为一个分布式协调框架,其内部存储 的都是一些关乎分布式系统运行时状态的元数据,尤其是一些涉及分布式锁、 Master 选 举和分布式协调等应用场景的数据,会直接影响基于 ZooKeeper 进行构建的分布式系统 的运行状态。因此,如何有效地保障 ZooKeeper 中数据的安全,从而避免因误操作而带 来的数据随意变更导致的分布式系统异常就显得格外重要了。所幸的是, ZooKeeper 提 供了一套完善的 ACL (Access Control List)权限控制机制来保障数据的安全。

提到权限控制,我们首先来看看大家都熟悉的、在 Unix/Linux 文件系统中使用的,也是 目前应用最广泛的权限控制方式—— UGO (User 、 Gro 叩和 Others) 权限控制机制。简 单地讲, UGO 就是针对一个文件或目录,对创建者 (User) 、创建者所在的组 (Gro 叩) 和其他用户 (Other) 分别配置不同的权限。从这里可以看出, UGO 其实是一种粗粒度 的文件系统权限控制模式,利用UGO 只能对三类用户进行权限控制,即文件的创建者、 创建者所在的组以及其他所有用户,很显然, UGO 无法解决下面这个场景:

  • 用户 U1 创建了文件 F 希望 U1 所在的用户组 G1 拥有对 F 读写和执行的权限, 另一个用户组G2 拥有读权限,而另外一个用户 U3 则没有任何权限。

接下去我们来看另外一种典型的权限控制方式: ACL 。 ACL, 即访问控制列表,是一种 相对来说比较新颖且更细粒度的权限管理方式,可以针对任意用户和组进行细粒度的权 限控制。目前绝大部分 Unix 系统都已经支持了 ACL 方式的权限控制, Linux 也从 2.6 版本的内核开始支持这个特性。

ACL 介绍
ZooKeeper 的 ACL 权限控制和 Unix/Linux 操作系统中的 ACL 有一些区别,可以从 三个方面来理解 ACL 机制,分别是:权限模式 (Scheme), 授权对象 (ID) 和权限 (Permission), 通常使用 “scheme: id:permission” 来标识一个有效的 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" 形式的权限标识来进行权限配置,便于区分不同应用来进行 权限控制。

    当我们通过 “usemame:password" 形式配置了权限标识后, ZooKeeper 会对其先后 进行两次编码处理,分别是 SHA-1 算法加密和 BASE64 编码,其具体实现由 DigestAuthenticationProvider.generateDigest(St ring idPassword) 函数进行封装,使用该函数进行 “username:password” 编码的一个实例。

    import java.security.NoSuchAlgorithmException;
    import org.apache.zookeeper.server.auth.DigestAuthenticationProvider;
    // 对  U username:password" 进行编码
    public class DigestAuthenticationProviderUsage {
    	public static void main( St ring[] args ) throws NoSuchAlgorithmException {
    		System.out.printin( DigestAuthenticationProvider.generateDigest( "foo:z k-book"));
    	}
    )
    //运行程序,输出结果如下:
    //foo:kWN6aNSbjcKWPqjiV7cg0N24raU=
    

    从上面的运行结果中可以看出, “username:password” 最终会被混淆为一个无法辨 识的字符串。

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

  • Super
    Super 模式,顾名思义就是超级用户的意思,也是一种特殊的 Digest 模式。在 Super 模式下,超级用户可以对任意 ZooKeeper ± 的数据节点进行任何操作。关于 Super 模式的用法,

2、授权对象: ID

授权对象指的是权限赋予的用户或一个指定实体,例如 IP 地址或是机器等。在不同的 权限模式下,授权对象是不同的,表 7-4 中列出了各个权限模式和授权对象之间的对应 关系。
在这里插入图片描述

3、权限: Permission

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

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

4、权限扩展体系

在上文中,我们已经讲解了 ZooKeeper 默认提供的 IP 、 Digest, World 和 Super 这四种 权限模式,在绝大部分的场景下,这四种权限模式已经能够很好地实现权限控制的目的。 同时, ZooKeeper提供了特殊的权限控制插件体系,允许开发人员通过指定方式对 ZooKeeper 的权限进行扩展。这些扩展的权限控制方式就像插件一样插入到 ZooKeeper 的权限体系中去,因此在ZooKeeper 的官方文档中,也称该机制为 "Pluggable ZooKeeper Authentication” 。

实现自定义权限控制器

  • 要实现自定义权限控制器非常简单, ZooKeeper 定义了一个标准权限控制器需要实现的 接口:org . apache. zookeeper. server. auth .Authenticationprovider
    public interface Authenticationprovider {
    	String getScheme();
    	KeeperException.Code handleAuthentication(ServerCnxn cnxn, byte authData[]);
    	boolean matches(String id, String aclExpr):
    	boolean isAuthenticated();
    	boolean isValid(String id);
    }
    
  • 用户可以基于该接口来进行自定义权限控制器的实现。事实上,在前面内容中提到的几 个权限模式,对应的就是 ZooKeeper 自带的 DigestAuthenticationProvider 和 IPAuthenticat ionProvider 两个权限控制器。

注册自定义权限控制器

  • 完成自定义权限控制器的开发后,接下去就需要将该权限控制器注册到 ZooKeeper 服务 器中去了。ZooKeeper 支持通过系统属性和配置文件两种方式来注册自定义的权限控制 器。

    • 系统属性 -Dzookeeeper .authProvider.X
      在 ZooKeeper 启动参数中配置类似于如下的系统属性:-Dzookeeper.authProvider.l=com.zkbook.CustomAuthenticationProvider

    • 配置文件方式
      在 zoo.cfg 配置文件中配置类似于如下的配置项:authProvider.l=com.zkbook.CustomAuthenticationProvider

  • 对于权限控制器的注册, ZooKeeper 采用了延迟加载的策略,即只有在第一次处理包含 权限控制的客户端请求时,才会进行权限控制器的初始化。同时, ZooKeeper 还会将所 有的权限控制器都注 册 到 ProviderRegistry 中 去 。 在 具 体 的 实 现 中 , ZooKeeper 首 先 会 将DigestAuthenticationProvider 和 IPAuthenticat ion Provider 这两 个默认的控制器初始化,然后通过扫描 zookeeper.authProvider. 这一系统属性, 获取到所有用户配置的自定义权限控制器,并完成其初始化。

ACL 管理
讲解完 ZooKeeper 的 ACL 及其扩展机制后,我们来看看如何进行 ACL 管理。

1、设置 ACL

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

[zk: localhost CONNECTED 2] create -e /zk-book init digest: foo:
MiGs3EiylpP4rvHlQlNwbP+oUF8=:cdrwa
Created /zk-book
[zk: localhost CONNECTED 3] getAcl /zk-book
'digest,'foo:MiGs3EiylpP4rvHlQlNwbP+oUF8=
:cdrwa

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

[zk: localhost CONNECTED 0]  create -e /zk-book init
Created /zk-book
[zk: localhost CONNECTED 1]  setAcl /zk-book digest :foo:MiGs3EiylpP4rvHlQlNwbP+oUF8=:
cdrwa
cZxid = 0x400000042
ctime = Sun Jul 13 22:14:13 CST 2014
mZxid = 0x400000042
mtime = Sun Jul 13 22:14:13 CST 2014
pZxid = 0x400000042
eversion = 0
dataVersion = 0
aclVersion = 1
ephemeralOwner = 0xl472ff49b020003
dataLength = 4
numChildren = 0 ‘
[zk: localhost CONNECTED 3]  getAcl /zk-book
'digest ; 'foo:MiGs3EiylpP4rvHlQlNwbP+oUF8=
:cdrwa

Super 模式的用法
根据 ACL 权限控制的原理,一旦对一个数据节点设置了 ACL 权限控制,那么其他没有 被授权的ZooKeeper 客户端将无法访问该数据节点,这的确很好地保证了 ZooKeeper 的 数据安全。但同时,ACL 权限控制也给 ZooKeeper 的运维人员带来了一个困扰:如果一 个持久数据节点包含了 ACL 权限控制,而其创建者客户端已经退出或已不再使用,那 么这些数据节点该如何清理呢?这个时候,就需要在 ACL 的 Super 模式下,’使用超级 管理员权限来进行处理了。

要使用超级管理员权限,首先需要在 ZooKeeper 服务器上开 启 S 叩 er 模式,方法是在 ZooKeeper 服务器启动的时候,
添加如下系统属性:-Dzookeeper.DigestAuthenticationProvider.superDigest=foo:kWN6aNSbjcKWPqjiV7c gON24raU=

  • 其中, “foo” 代表了一个超级管理员的用户名,wkWN6aNSbjcKWPqjiV7cgON24raU=w 是 可变的,由 ZooKeeper 的系统管理员来进行自主配置
  • 此例中使用的是 "foo:zk-book” 的编码。完成对ZooKeeper 服务器的 Super 模式的开启后,就可以在应用程序中使用了
import org.apache.zookeeper.CreateMode;
import org.apache.zookeeper.ZooDefs.Ids;
import org.apache.zookeeper.ZooKeeper;

//使用 Super 权限模式进行权限控制
public class AuthSample_Super {
	final static String PATH = "/zk-book";
	public static void main(String!] args) throws Exception {
		ZooKeeper zookeeperl = new ZooKeeper("domainl.book.zookeeper:2181",5000, null);
		zookeeperl.addAuthlnfo("digest", "foo:true".getBytes());
		zookeeperl.create( PATH, "init".getBytes(), Ids.CREATOR ALL ACL, CreateMode.EPHEMERAL );
		
		ZooKeeper zookeeper2 = new ZooKeeper("domainl.book.zookeeper: 2181",50000, null);
		zookeeper2.addAuthlnfo("digest", "foo:zk-book".getBytes());
		System.out.printin(zookeeper2.getData( PATH, false, null ));
		
		ZooKeeper zookeeper3 = new ZooKeeper("domainl.book.zookeeper: 2181",50000, null);
		zookeeper3. addAuthlnfo("digest", "foo:false".getBytes ());
		System.out.printin(zookeeper3.getData( PATH, false, null ));
	}
}
//运行程序,输出结果如下:
//[B(a7b7072
//org.apache.zookeeper.KeeperException$NoAuthException:KeeperErrorCode = NoAuth for /zk-book

从上面的输出结果中,我们可以看出,由于 “foo:zk-book” 是一个超级管理员账户,因 此能够针对一个受权限控制的数据节点 zk-book 随意进行操作,但是对于 “foo:false” 这 个普通用户,就无法通过权限校验了。

相关推荐
©️2020 CSDN 皮肤主题: 撸撸猫 设计师:马嘣嘣 返回首页