Zookeeper基础

500 阅读8分钟

一、Zookeeper概述

1.1)概述

Zookeeper是用于分布式应用程序的协调服务框架。是Apache Hadoop的一个子项目,主要用于解决分布式应用中经常遇到的一些数据管理问题;如:统一命名服务、状态同步服务、集群管理、分布式应用配置项的管理(目前分布式的应用服务配置项大部分使用的是阿里的Nacos)等; Zookeeper是一个文件系统的数据结构(即:树形结构)体现,是通过节点的形式进行体现的;

公开了一组简单的API,分布式应用程序可以基于这些API用于同步、节点状态、配置信息、服务注册信息等; 由Java编写,支持Java及C两种语言的客户端;

1.2)Zookeeper能做什么

基于Zookeeper的特性,那么利用Zookeeper可以用作以下几个方面:

  1. 多节点任务任务分配;
  2. RPC调用时的服务注册与发现;
  3. 保证并发请求的幂等;

必须由一个独立的服务做协调工作,必须可靠而且保证性能;

二、核心基础

2.1)节点(znode)

记录数据是管理的总要概念;所有数据都是节点; 节点:数据基本单元; 节点下可以包含子节点,最后以树的形式呈现。每个节点拥有唯一的路径PATH。客户端基于PATH上传节点数据,收到后会实时通知对该路径进行监听的客户端;

znode结构如下:

  • path:唯一路径;
  • childNode:子节点;
  • stat:状态属性;
  • type:节点类型;

节点类型说明:

  • PERSISTENT 持久节点;客户端与Zookeeper断开后,节点依旧存在,只要不手动删除,将永远存在;
  • PERSISTENT_SEQUENTIAL 持久序号节点;与持久化节点类似,只是Zookeeper给该节点名称进行顺序编号,是依次递增的;
  • EPHEMERAL 临时节点;从节点是由服务端进行维护的,客户端与Zookeeper断开后,节点将被删除;可以设置过期时间,但是这个过期时间是由服务端进行控制,通过协商而定,并非单方面由客户端指定;此类型节点不能再拥有子节点,断开连接后就关闭,可以用于心跳,服务注册;
  • EPHEMERAL_SEQUENTIAL 临时序号节点;这里注意“是有序的”,大致与“PERSISTENT_SEQUENTIAL”相似,类型为临时的,非持久;
  • Container 在3.5.3版本后新增的特性,如果Container节点下面没有子节点,则Container节点在未来被Zookeeper自动清除,定时任务默认是60S检查一次;是一个容器节点,一个容器下会承载很多子节点,如果创建了一个容器(Container)节点,但是下面没有子节点,那么Zookeeper会在一定时间内进行清除;可以实现分布式锁;
  • TTL 默认禁用,只能通过系统配置:Zookeeper.extendedTypesEnabled=true开启,注意:此类型节点不稳定;

2.2)事件监听机制

在客户端可以注册监听器用于关注客户端本身关心的任意节点或者目录以及递归子目录节点;

  1. 某个节点的监听:当这个节点被删除、修改时,对应的监听客户端将会收到通知;
  2. 某个目录的监听:当这个目录有子节点被创建或者子节点被删除时,对应的客户端将会收到通知;
  3. 某个目录的递归子节点进行监听:当这个目录下面的任意子节点有目录结构的变化(子节点被创建、删除或者根节点有数据变化)时,对应的监听客户端将会收到通知;

注意:无论是对节点还是目录监听都是一次性的,一旦触发,对应的监听即被删除;递归子节点,监听是对所有子节点的,所以,只要有任意一个节点触发通知,那么当前的监听也会被删除;

三、Zookeeper的经典分布式应用场景

  1. 分布式配置中心;
  2. 分布式注册中心;
  3. 分布式锁;
  4. 分布式对列;
  5. 集群选举;
  6. 分布式屏障;
  7. 发布/订阅;

四、Zookeeper实战

4.1)检查Java环境

java -version

下面以Zookeeper-3.5.8版本为例,需要Java版本为JDK8及以上;

4.2)下载并解压Zookeeper

wget https://mirror.bit.edu.cn/apache/zookeeper/zookeeper-3.5.8/apache-zookeeper-3.5.8-bin.tar.gz
tar -zxvf apache-zookeeper-3.5.8.tar.gz
cd apache-zookeeper-3.5.8-bin

4.3)配置文件处理

将配置文件conf/zoo_sample.cfg文件拷贝一份名为:zoo.cfg

cp ./conf/zoo_sample.cfg ./conf/zoo.cfg

配置文件说明:

# 最小的时间单位,真个Zookeeper默认的都是基于这个参数
tickTime=2000
# 一般是在数据同步中使用,这个参数值乘以tickTime参数
initLimit=10
# 集群时的心跳检查时间
syncLimit=5
# 节点的持久化位置,还有一些快照数据等等
#dataDir=/tmp/zookeeper
dataDir=/tools/zookeeper/apache-zookeeper-3.5.8/data
# 客户端访问的端口
clientPort=2181
# the maximum number of client connections.
# increase this if you need to handle more clients
#maxClientCnxns=60
#
# Be sure to read the maintenance section of the 
# administrator guide before turning on autopurge.
#
# http://zookeeper.apache.org/doc/current/zookeeperAdmin.html#sc_maintenance
#
# The number of snapshots to retain in dataDir
#autopurge.snapRetainCount=3
# Purge task interval in hours
# Set to "0" to disable auto purge feature
#autopurge.purgeInterval=1
#Zookeeper3.5新特性,如果启动发生端口被占用,这里修改启动端口
admin.serverPort=9191

4.4)启动Zookeeper

./bin/zkServer.sh start ./conf/zoo.cfg
./bin/zkServer.sh stop ./conf/zoo.cfg

可以使用./bin/zkServer.sh来查看参数

4.5)客户端链接

./bin/zkCli.sh

远程链接

./bin/zkCli.sh -server 【远程主机IP:端口】

五、相关命令

5.1)帮助

help
###########结果如下###########
ZooKeeper -server host:port cmd args
	addauth scheme auth
	close 
	config [-c] [-w] [-s]
	connect host:port
    #创建节点;-s:创建的节点是带序号的,序号是递增的;-e:临时节点;-c:容器节点;-t:ttl节点;data:节点下的数据内容,可以不提供;acl:权限信息;
    #不加任何参数为持久化节点;
	create [-s] [-e] [-c] [-t ttl] path [data] [acl]
   #删除
	delete [-v version] path
   #批量删除路径下的所有数据
	deleteall path
	delquota [-n|-b] path
   #获取路径下的数据; -w:加监听;
	get [-s] [-w] path
	getAcl [-s] path
	history 
	listquota path
   #查看目录,必须跟上路径;-w:添加路径监听;
	ls [-s] [-w] [-R] path
	ls2 path [watch]
	printwatches on|off
	quit 
	reconfig [-s] [-v version] [[-file path] | [-members serverID=host:port1:port2;port3[,...]*]] | [-add serverId=host:port1:port2;port3[,...]]* [-remove serverId[,...]*]
	redo cmdno
   #删除监听
	removewatches path [-c|-d|-a] [-l]
	rmr path
   #设置节点下的参数;-s:显示元数据信息(包括:事务ID;修改时间等)
	set [-s] [-v version] path data
	setAcl [-s] [-v version] [-R] path acl
	setquota -n|-b val path
	stat [-w] path
	sync path
Command not found: Command not found help

5.2)查看节点

“/”:标识根节点;-R:标识查看所有节点;

ls /
ls -R
ls -R 【路径】

六、权限控制

Zookeeper的权限控制就是ACL(Access Control List)的实现;可以控制节点的读写操作,保证数据的安全性,Zookeeper ACL的配置分为:权限模式(Scheme)、授权对象(ID)、权限信息(Permission)3个部分组成;最终组成一条综合的ACL信息(例如:Scheme:ID:Permission);

6.1)权限模式(Scheme)

用来设置Zookeeper服务器进行权限验证的方式,大致权限分为“范围验证”、“口令验证”两种; 范围验证: Zookeeper可以针对一个IP或者一段IP地址授予某种权限;

口令验证: 可以理解为用户名密码模式。在Zookeeper中这种验证方式是Digest认证,而Digest这种认证方式首先在客户端传送“username:password”这种形式的授权标识符,Zookeeper服务端会对密码部分使用SHA-1和BASE64算法进行加密,以保证安全性;

Super权限模式: 超级管理员模式,这种模式可以认为是一种特殊的Digest认证,具有Super权限的用户端可以对Zookeeper上的任意数据节点进行任意操作;配置在bin目录下的zkService.sh中进行配置,在“ZOOMAIN”中加入“-Dzookeeper.DigestAuthenticationProvider.superDigest=【用户名】:<base64encoded(SHA1(【密码】)) ”;

注意:没有指定任何权限的情况下,默认的使用“word”进行标识,权限为“anyone”,表示任何人都可以访问及操作;

6.2)授权对象(ID)

就是指权限赋予的目标,而对于4种不同的权限模式来说,如果选择的是IP方式,使用授权对象可以是一个IP或者一个IP段;如果使用Digest或Super,则对应一个用户名;如果是World模式,授权系统中所有的用户;

6.3)权限信息(Permission)

是指可以在数据节点上执行的操作种类,Zookeeper中已经定义好的权限有:

权限类型权限标识说明
数据节点c创建权限(create);授予权限的对象可以在数据节点下创建子节点;
数据节点w更新权限(wirte);授予权限的对象可以更新该节点;
数据节点r读取权限(read);授予权限的对象可以读取节点的内容以及子节点的列表信息;
数据节点d删除权限(delete);授予权限的对象可以删除该节点的子节点;
数据节点s管理者权限(admin);授予权限的对象可以对节点进行ACL配置;

6.4)操作命令

getAcl: 获取某节点的ACL信息;

setAcl: 设置某节点的ACL信息,清空节点下的权限:setAcl 【节点】 world:anyone:cdwra

addauth: 输入认证授权信息,相当于注册用户信息,注册时输入明文密码,Zookeeper将以密文的形式存储;

注意:在系统参数中zookeeper.skipACL=yes的时候,标识不再进行权限检测;即:就算配置了权限也无效;配置在bin目录下的zkService.sh中在“ZOOMAIN”中加入“-Dzookeeper.skipACL=yes ”即可;

6.5)实战

Linux脚本模式

echo -n 【用户名】:【密码】 | openssl dgst -binary -sha1 | openssl base64

在Zookeeper中进入客户端,以用户名密码为例,使用下面的命令创建节点并设置开放的权限;

#密文授权方式
create 【节点名称】 【数据】 digest:【用户名】:【密钥信息】:【权限信息,全部赋予:cwrda;只读权限:r】
#明文授权方式,注意使用明文方式,必须先登录;
addauth digest 【用户名】:【密码】
create 【节点名称】 【数据】 auth:【用户名】:【密钥信息】:【权限信息,全部赋予:cwrda;只读权限:r】
#IP授权方式,一个网段的配置:192.168.1.1/24
create 【节点名称】 【数据】 ip:【ip地址】:【权限信息,全部赋予:cwrda;只读权限:r】
#多个权限配置
create 【节点名称】 【数据】 ip:【ip地址】:【权限信息,全部赋予:cwrda;只读权限:r】,ip:【ip地址】:【权限信息,全部赋予:cwrda;只读权限:r】

#操作时需要先登录
addauth digest 【用户名】:【密码】

#IP授权模式
setAcl 【节点名称】 【数据】

七、数据结构

Zookeeper数据都是存储在内存中的,所以数据操作很快;其数据结构模拟如下:

ConcurrentHashMap<String, DataNode>

其中Key就是我们说的节点,DataNode就是我们的数据;DataNode是Zookeeper存储节点数据的最小单元,大致数据结构如下:

class DataNode implements Record{
    //用于存放业务数据
    byte[] data;
    //权限信息
    Long acl;
    //状态信息
    StatPersisted stat;
    //子节点数据
    Set<String> children = null;
}

八、数据存储

针对每一次客户端的事务操作,Zookeeper都会将它们记录到事务日志中,同时也会将数据的变更应用到内存数据库中;可以在Zookeeper的主配置文件zoo.cfg中配置内存中数据持久化目录,也就是事务日志存放的路径dataLogDir;如果没有配置dataLogDir,事务日志将存放到dataDir目录;同时还会提供一个snapshot快照文件(是某一时刻的所有内存中的数据);

Zookeeper生成事务日志的时候,会采用一种叫“预分配”的机制,这个机制就是对平凡磁盘读写的一个优化,一次性开辟一个比较大的空间,使用追加的方式逐步写入,以提高效率,因为在不断的写入过程中底层磁盘IO会不断的为其开辟磁盘块(磁盘Seek),这个操作是很耗时的;预分配磁盘的大小可以通过参数zookeeper.preAllocSize进行配置;

Zookeeper提供了一个日志格式化工具(org.apache.zookeeper.server.LogFormatter),可以通过这个工具查看事务日志中的数据;

#进入Zookeeper的lib目录
java -cp slf4j-api-1.7.25.jar:zookeeper-3.5.8.jar:zookeeper-jute-3.5.8.jar org.apache.zookeeper.server.LogFormatter 【文件路径】

java -classpath .:slf4j-api-1.7.25.jar:zookeeper-3.5.8.jar:zookeeper-jute-3.5.8.jar org.apache.zookeeper.server.LogFormatter 【文件路径】

Zookeeper提供了一个快照格式化工具(org.apache.zookeeper.server.SnapshotFormatter),可以通过这个工具查看快照中的数据;

#进入Zookeeper的lib目录
java -cp slf4j-api-1.7.25.jar:zookeeper-3.5.8.jar:zookeeper-jute-3.5.8.jar org.apache.zookeeper.server.SnapshotFormatter 【文件路径】

快照是Zookeeper服务器上某一时刻的全量数据,可以通过snapCount配置每间隔事务请求数,用以生成快照;快照生成的时机规则算法为:【snapCount/2+随机数(随机范围1-snapCount/2)】;这么做的用意在于集群环境下,不至于所有服务在同一时间开始做快照;