大名鼎鼎的Zookeeper

1. ZooKeeper 是什么

  • Apache 的顶级项目
  • 为分布式应用提供高效且可靠的分布式协调服务,诸如统一命名服务、配置管理和分布式锁等
  • 主要用来解决分布式集群中应用系统的一致性问题,它能提供基于类似于文件系统的目录节点树方式的数据存储。
  • 但是它并不是用来专门存储数据的,主要是用来维护和监控存储数据的状态变化。通过监控这些数据状态的变化,从而达到基于数据的集群管理
  • 很多大名鼎鼎的框架都基于 ZooKeeper 来实现分布式高可用,如:Dubbo、Kafka 等

可以理解为 Zookeeper 是一个用于存储少量数据的基于内存 的数据库,主要有如下两个核心的概念:文件系统数据结构+监听通知机制

经典的应用场景

  • 分布式配置中心
  • 分布式注册中心
  • 分布式锁
  • 分布式队列
  • 集群选举
  • 分布式屏障
  • 发布/订阅

2. 数据结构

文件系统数据结构

主要包括节点、路径、数据和版本等信息

  1. 节点:树形结构中每个节点称为Znode,是数据存储的最小单位,分为持久和临时两大类
  2. 路径:每个Znode都有一个路径。节点路径由斜杠分隔的一系列名称组成,例如 /path/to/node。没有相对路径的说法
  3. 数据:每个节点都存储着与之相关联的数据,该数据是一个字节数组(byte[])。
  4. 版本号:数据发生变化时,版本号会递增

作为ZooKeeper的作者,当初选择采用文件系统数据结构的设计主要考虑了以下几个因素:

  1. 易于理解:文件系统是一种非常常见的数据结构,对于开发人员来说是非常容易理解和掌握的。
  2. 分布式一致性:可以比较简单地实现数据的版本控制和分布式锁等机制来保证分布式系统的数据一致性。

每个Znode都有一个全局唯一的路径名,并且任何更改都必须通过ZooKeeper API完成。

     3. 扩展性:文件系统的数据结构由于其层级结构的特点,很容易扩展,增加新的节点和子节点不会影响到现有节点的性能和稳定性

     4. 监控和通知:文件系统对于节点的信息和变化的监控能力非常强,能够比较容易地实现观察者模式等通知机制。

3. Znode

  • 类型

  • 元数据

cZxid:即Created Zxid,增、删、改等都是事务请求,Get不是。因为事务ID是递增的,所以也能做到顺序性

mZxid:修改这个节点的事务ID

pZxid:子节点列表发生变化时,事务ID会发生变化

cversion:子节点列表的版本号

dataversion:数据版本号,即乐观锁的实现

aclVersion:权限控制的版本号

ephemeralOwner:16进制串,创建临时节点时的session id。非临时节点,值都是0没有意义

如果创建临时节点的客户端,关闭并超过30秒,会打印出如上日志。临时节点会被删除,如果想再次获取,会报错

注意:临时节点是不能创建子节点的

4. 监听通知机制

客户端注册监听它关心的 znode,当 znode 状态发生变化(数据变化、子节点增减变化)时,ZooKeeper 服务会通知客户端

一般有两种形式:

  • 客户端向服务端不断轮询
  • 服务端向客户端推送状态

Zookeeper 的选择是服务端主动推送状态,也就是观察机制( Watch )。

通知机制的实现其实还是比较简单的,通过读请求设置 Watcher 监听事件,写请求在触发事件时就能将通知发送给指定的客户端。

第一个是Znode目录的监听,如果只是修改Znode数据,是不会触发监听的,只有节点列表发生变化时,才会触发监听

第二个是对Znode数据的监听,即使设置的值是一样的,也会触发监听,因为元数据比如版本号还是会发生变化

递归监听所有的Znode目录

注意:监听只会触发一次。减轻服务端的压力

5. 权限控制

权限信息

  • 数据节点(c: create)创建权限,授予权限的对象可以在数据节点下创建子节点
  • 数据节点(w: wirte)更新权限,授予权限的对象可以更新该数据节点
  • 数据节点(r: read)读取权限,授予权限的对象可以读取该节点的内容以及子节点的列表信息
  • 数据节点(d: delete)删除权限,授予权限的对象可以删除该数据节点的子节点
  • 数据节点(a: admin)管理者权限,授予权限的对象可以对该数据节点体进行 ACL 权限设置

        1. 密文授权

// Java代码生成授权ID
 @Test
public void generateSuperDigest() throws NoSuchAlgorithmException {
    String sId = DigestAuthenticationProvider.generateDigest("gj:test");
    System.out.println(sId);    // gj:X/NSthOB0fD/OT6iilJ55WJVado=
}
# 通过xshell生成  生成授权ID
[root@izbp162zdrsa1yuxrxom5cz bin] echo -n yph:931007 | openssl dgst -binary -sha1 | openssl base64
JuExe5J8h0K4yrUdrJRWnjR2m7E=
# 设置权限
create /test/yph 666 digest:yph:JuExe5J8h0K4yrUdrJRWnjR2m7E=:rw
# 或者
setAcl /zk‐node digest:gj:X/NSthOB0fD/OT6iilJ55WJVado=:cdrwa

# 此时获取节点数据报错
[zk: localhost:2181(CONNECTED) 8] get /test/yph
org.apache.zookeeper.KeeperException$NoAuthException: KeeperErrorCode = NoAuth for /test/yph

# 使用如下命令,登录后就能访问。
addauth digest yph:931007 

addauth 是与当前Session 绑定的,退出后就失效

        2. 明文授权

addauth digest panzhuping:950219 #注册登录
create /test/pzp 888 auth:panzhuping:950219:rw # 然后明文授权

        3. IP授权模式

create /node‐ip data ip:192.168.109.128:cdwra
#或者 多个指定IP可以通过逗号分隔,如 ip:IP1:rw,ip:IP2:cdw
setAcl /node‐ip ip:192.168.109.128:cdwra

         4. 跳过权限

# vim zkServer.sh 添加系统参数
-Dzookeeper.skipACL=yes #跳过限检测,默认是no

         5. 添加超级管理员(修改权限)

# echo -n admin:123456 | openssl dgst -binary -sha1 | openssl base64:生成 SHA1之后再用Base64加密的密文比如 xxxx
# vim zkServer.sh 添加系统参数
‐Dzookeeper.DigestAuthenticationProvider.superDigest=admin:xxxx

#登录超级管理员用户
addauth digest admin:123456

6. 底层数据结构与持久化

  • DataTree 底层数据结构,数据都是存在内存中的。String 就是Path,DataNode 就是节点
public class DataTree {
    private final ConcurrentHashMap<String, DataNode> nodes =
    new ConcurrentHashMap<String, DataNode>();
    private final WatchManager dataWatches = new WatchManager();
    private final WatchManager childWatches = new WatchManager();
  • DataNode是Zookeeper存储节点数据的最小单位,byte data[] 以字节的方式存储数据,StatPersisted 就是存储节点的元数据信息
public class DataNode implements Record {
    byte data[];
    Long acl;
    public StatPersisted stat;
    private Set<String> children = null;

  • 持久化(事务日志)

客户端每一次的事务操作(创建节点、建立连接等),会记录事务日志,可以手动配置存储路径,比如dataLogDir。但是如果没有配置,会放在dataDir目录下

为什么建立连接也是事务请求:

        如果在集群环境下,当客户端与A机器建立连接,Session也会同步到B机器,此时假设当A机器出现宕机,并且客户端在Session超时范围内,又重新连接到B机器上时,因为session不会变,还是能看到临时节点,继续业务操作

磁盘的预分配机制:

        事务日志不断追加写操作会触发底层磁盘IO为文件开辟新的磁盘块,因此为了提升磁盘IO的效率,ZK在创建日志文件的时候会向操作系统申请一块大一点的磁盘块。这个预分配的磁盘大小可以通过系统参数 zookeeper.preAllocSize 进行配置

事务日志文件名为:

        log.,因为日志文件是顺序写入的,所以这个最大事务ID也是整个事务日志文件中最小的事务ID,日志满了即下一次事务日志文件的创建

  • 查看事务日志的格式化工具

org.apache.zookeeper.server.LogFormatter

slf4j-api-1.7.25.jar 格式化二进制数据

zookeeper-jute-3.5.8.jar 序列化的工具

java -classpath .:slf4j-api-1.7.25.jar:zookeeper-3.5.8.jar:zookeeper-jute-3.5.8.jar org.apache.zookeeper.server.LogFormatter /opt/zookeeper/data/version-2/log.31

  •  数据快照

可以通过配置snapCount配置每间隔事务请求个数,生成快照,数据存储在dataDir 指定的目录中,

为了避免集群中所有机器在同一时间进行快照,实际的快照生成时机为事务数达到 [snapCount/2 + 随机数(随机数范围为1 ~ snapCount/2 )] 个数时,生成快照

快照事务日志文件名为: snapshot.,日志满了即进行下一次事务日志文件的创建

有了事务的日志,为什么还要数据快照:

        某一时刻的全量内存数据,为了快速恢复,可以先恢复快照数据,但是通常快照数据是反应当时内存数据的状态。事务日志是更全面的数据,再通过增量恢复事务日志中的数据

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值