zookeeper是什么?
看了官网的解释后,还是不知道zookeeper是干什么的,所以我尝试拆分上述,编了故事。
我担任了一家动物园的管理员的职务,动物园里面有很多动物,我希望这些动物每天有序的,高效的喝水。
这些动物每天10点喝水,本来想给每个动物都安排一个喝水的地方,但随着动物数量的增加,保证每一个动物按时喝水,对我来说难度太大了,3000的工资不至于这么累。
所以我想了一个办法弄了一个共同的喝水的地方,确保所有动物都能唯一且明确地识别出喝水地,我给这个地方做了一个标记-天池。我发现体积大的动物不让体积小的动物喝水,所以我弄了一个临时处,让所有动物必须先到这里拿到牌子,才能喝水,喝完水把牌子放回去。
因为大象是所有动物中体积最大的,我把大象任命为领导,所有动物如果想天池中注水,都先给到大象,大象负责注水,注水完成后,其他动物收到注水完成消息继续进行喝水操作。
-
喝水地的标记与命名服务:可以把“喝水地”的标记看作是ZooKeeper提供的命名服务,确保所有动物(系统中的各个服务或组件)都能唯一且明确地识别出共享资源的位置。
-
临时处与分布式锁:动物需先到“临时处”拿牌子再喝水的过程,完美映射了ZooKeeper中的分布式锁机制。这里的“牌子”就像是锁,确保每次只有一个动物能喝水,避免资源冲突。动物喝完水归还牌子,即释放锁,下一个动物才能获取锁进行操作。
-
大象作为领导的选举:大象被任命为“领导”,负责注水,这一过程体现了ZooKeeper的Leader选举功能。在真实的ZooKeeper集群中,会选举出一个Leader节点来协调和管理整个集群的状态更新,确保数据一致性。
-
消息通知与Watcher机制:当大象完成注水后,其他动物收到消息继续喝水,这反映了ZooKeeper的Watcher机制。Watcher允许客户端注册监听某个ZNode的变化,一旦变化发生,ZooKeeper会通知所有订阅的客户端,从而实现快速响应和同步。
-
资源调度与有序访问:考虑到动物体积大小导致的优先级问题,可以进一步类比为ZooKeeper在控制分布式系统中任务调度的有序性。通过配置管理,可以设定特定的服务或任务优先级,保证系统的高效运作。
那zookeeper到底是什么,有什么用?
严格定义:zooKeeper致力于为分布式应用提供一个高性能、高可用,且具有严格顺序访 问控制能力的分布式协调服务。
高性能:量数据存储在内存中;
高可用:zooKeeper一般以集群的方式对外提供服务;
严格顺序访问:对于每个更新请求,ZooKeeper都会分配一个全局唯一的递增编号。
zookeeper怎么存储数据呢?
上述树中有很多节点,每个节点都有子节点。zookeeper节点也是树状结构,节点叫做znode。
那么如何描述一个znode呢?
znode分三个部分:
节点的数据:znode data,节点的子节点,节点的状态;
节点的状态包含一下信息:
[zk: localhost:2181(CONNECTED) 7] get /ns-1/tenant
cZxid = 0x6a0000000a
ctime = Wed Mar 27 09:56:44 CST 2019
mZxid = 0x6a0000000a
mtime = Wed Mar 27 09:56:44 CST 2019
pZxid = 0x6a0000000e
cversion = 2
dataVersion = 0
aclVersion = 0
ephemeralOwner = 0x0
dataLength = 0
numChildren = 2
cZxid | 数据节点创建时的事务 ID |
ctime | 数据节点创建时的时间 |
mZxid | 数据节点最后一次更新时的事务 ID |
mtime | 数据节点最后一次更新时的时间 |
pZxid | 数据节点的子节点最后一次被修改时的事务 ID |
cversion | 子节点的更改次数 |
aclVersion | 节点的 ACL 的更改次数 |
ephemeralOwner | 如果节点是临时节点,则表示创建该节点的会话的 SessionID;如果节点是持久节点,则该属性值为 0 |
dataLength | 数据内容的长度 |
numChildren | 数据节点当前的子节点个数 |
zookeeper 安装
环境信息:(自行下载,官网一般都能找到)
jdk版本 | jdk:jdk-8u131-linux-x64.tar.gz |
linux版本 | centos7.3 |
zookeeper版本 | zookeeper:zookeeper-3.4.10.tar.gz |
1、为了演示方便,先在centos7.3创建一个zookeeper用户:
用户名 | zookeeper |
密码 | zookeeper |
2、因为zookeeper底层依赖jdk,登录zookeeper用户,安装jdk:
tar -xzvf jdk-8u131-linux-x64.tar.gz
3、配置jdk环境变量:
vi .bash_profile
// 文件中加入如下内容
JAVA_HOME=/home/zookeeper/jdk1.8.0_131
export JAVA_HOME
PATH=$JAVA_HOME/bin:$PATH
export PATH
// 使环境变量生效
. .bash_profile
4、检测jdk安装:
java -version
5、解压zookeeper:
tar -xzvf zookeeper-3.4.10.tar.gz
6、准备配置文件:
1、先进入conf目录
cd /home/zookeeper/zookeeper-3.4.10/conf
2、复制该配置文件
cp zoo_sample.cfg zoo.cfg
3、创建目录
mkdir data
4、指定存储zookeeper内存快照和日志地址
dataDir=/home/zookeeper/zookeeper-3.4.10/data
7、启动zookeeper:
// 进入zookeeper的bin目录
cd /home/zookeeper/zookeeper-3.4.10/bin
// 启动zookeeper
./zkServer.sh start
//启动:zkServer.sh start
//停止:zkServer.sh stop
//查看状态:zkServer.sh status
zookeeper常用Shell命令
新增节点
create [-s] [-e] path data #其中-s 为有序节点,-e 临时节点
//创建持久化节点并写入数据
create /hadoop "123456"
//创建持久化有序节点,此时创建的节点名为指定节点名 + 自增序号
[zk: localhost:2181(CONNECTED) 2] create -s /a "aaa"
Created /a0000000000
[zk: localhost:2181(CONNECTED) 3] create -s /b "bbb"
Created /b0000000001
[zk: localhost:2181(CONNECTED) 4] create -s /c "ccc"
Created /c0000000002
//创建临时节点,临时节点会在会话过期后被删除:
[zk: localhost:2181(CONNECTED) 5] create -e /tmp "tmp"
Created /tmp
//创建临时有序节点,临时节点会在会话过期后被删除:
[zk: localhost:2181(CONNECTED) 6] create -s -e /aa 'aaa'
Created /aa0000000004
[zk: localhost:2181(CONNECTED) 7] create -s -e /bb 'bbb'
Created /bb0000000005
[zk: localhost:2181(CONNECTED) 8] create -s -e /cc 'ccc'
Created /cc0000000006
更新节点
直接用set进行更新:
[zk: localhost:2181(CONNECTED) 3] set /hadoop "345"
cZxid = 0x4
ctime = Thu Dec 12 14:55:53 CST 2019
mZxid = 0x5
mtime = Thu Dec 12 15:01:59 CST 2019
pZxid = 0x4
cversion = 0
dataVersion = 1
aclVersion = 0
ephemeralOwner = 0x0
dataLength = 3
numChildren = 0
基于版本号更新:
[zk: localhost:2181(CONNECTED) 10] set /hadoop "3456" 1
version No is not valid : /hadoop
删除节点
delete path [version]
当你传入的数据版本号 (dataVersion) 和当前节点的数据版本号不符合时,zookeeper 不会执行删除操作。
[zk: localhost:2181(CONNECTED) 36] delete /hadoop 0
version No is not valid : /hadoop #无效的版本号
[zk: localhost:2181(CONNECTED) 37] delete /hadoop 1
[zk: localhost:2181(CONNECTED) 38]
查看节点
get path
[zk: localhost:2181(CONNECTED) 1] get /hadoop
123456
cZxid = 0x4
ctime = Thu Dec 12 14:55:53 CST 2019
mZxid = 0x4
mtime = Thu Dec 12 14:55:53 CST 2019
pZxid = 0x4
cversion = 0
dataVersion = 0
aclVersion = 0
ephemeralOwner = 0x0
dataLength = 6
numChildren = 0
查看节点状态
使用 节点数据 stat 命令查看节点状态:
[zk: localhost:2181(CONNECTED) 2] stat /hadoop
cZxid = 0x4
ctime = Thu Dec 12 14:55:53 CST 2019
mZxid = 0x4
mtime = Thu Dec 12 14:55:53 CST 2019
pZxid = 0x4
cversion = 0
dataVersion = 0
aclVersion = 0
ephemeralOwner = 0x0
dataLength = 6
numChildren = 0
查看节点列表
查看节点列表有 ls path 和 ls2 path 两个命令,ls2是ls1的增强,可以查看当前节点属性
[zk: localhost:2181(CONNECTED) 0] ls /
[cluster, controller_epoch, brokers, storm, zookeeper, admin, ...]
[zk: localhost:2181(CONNECTED) 1] ls2 /
[cluster, controller_epoch, brokers, storm, zookeeper, admin, ....]
cZxid = 0x0
ctime = Thu Jan 01 08:00:00 CST 1970
mZxid = 0x0
mtime = Thu Jan 01 08:00:00 CST 1970
pZxid = 0x130
cversion = 19
dataVersion = 0
aclVersion = 0
ephemeralOwner = 0x0
dataLength = 0
numChildren = 11
监听器get path [watch]
使用 get path [watch] 注册的监听器能够在节点内容发生改变的时候,向客 户端发出通知。但记住一次性的,只能监听一次,你想第二次监听必须再注册。
[zk: localhost:2181(CONNECTED) 4] get /hadoop watch
[zk: localhost:2181(CONNECTED) 5] set /hadoop 45678
WATCHER::
WatchedEvent state:SyncConnected type:NodeDataChanged path:/hadoop #节点
值改变
监听器stat path [watch]
使用 户端发出通知 stat path [watch] 注册的监听器能够在节点状态发生改变的时候,向客户端发出通知
[zk: localhost:2181(CONNECTED) 7] stat /hadoop watch
[zk: localhost:2181(CONNECTED) 8] set /hadoop 112233
WATCHER::
WatchedEvent state:SyncConnected type:NodeDataChanged path:/hadoop #节点
值改变
监听器ls\ls2 path [watch]
使用 ls path [watch] 或 所有子节点的增加和删除操作。
[zk: localhost:2181(CONNECTED) 9] ls /hadoop watch
[]
[zk: localhost:2181(CONNECTED) 10] create /hadoop/yarn "aaa"
WATCHER::
WatchedEvent state:SyncConnected type:NodeChildrenChanged path:/hadoop
zookeeper的acl权限控制
谁都可以修改节点,删除节点,那我这个系统也太不稳定了吧。我想给每个节点都设置对应权限该怎么操作呢?
权限模式:
方案 | 描述 |
world | 只有一个用户:anyone,代表登录zokeeper所有人(默认) |
ip | 对客户端使用IP地址认证 |
auth | 使用已添加认证的用户认证 |
digest | 使用“用户名:密码”方式认证 |
授予的权利:
权限 | ACL简写 | 描述 |
create | c | 创建 |
delete | d | 删除 |
read | r | 读取 |
write | w | 写 |
admin | a | 控制列表权限 |
授予的相关命令:
命令 | 使用方式 | 描述 |
getAcl | getAcl | 读取ACL权限 |
setAcl | setAcl | 设置ACL权限 |
addAuth | addAuth | 添加认证用户 |
world授权模式:
setAcl world:anyone:
[zk: localhost:2181(CONNECTED) 1] create /node1 "node1"
Created /node1
[zk: localhost:2181(CONNECTED) 2] getAcl /node1
'world,'anyone
#world方式对所有用户进行授权
: cdrwa
#删、改、查、管理
[zk: localhost:2181(CONNECTED) 3] setAcl /node1 world:anyone:drwa
cZxid = 0x2
ctime = Fri Dec 13 22:25:24 CST 2019
mZxid = 0x2
mtime = Fri Dec 13 22:25:24 CST 2019
pZxid = 0x2
cversion = 0
dataVersion = 0
aclVersion = 1
ephemeralOwner = 0x0
dataLength = 5
numChildren = 0
IP授权模式:
setAcl <path> ip:<ip>:<acl>
[zk: localhost:2181(CONNECTED) 18] create /node2 "node2"
Created /node2
[zk: localhost:2181(CONNECTED) 23] setAcl /node2
ip:192.168.60.129:cdrwa
cZxid = 0xe
ctime = Fri Dec 13 22:30:29 CST 2019
mZxid = 0x10
mtime = Fri Dec 13 22:33:36 CST 2019
pZxid = 0xe
cversion = 0
dataVersion = 2
aclVersion = 1
ephemeralOwner = 0x0
dataLength = 20
numChildren = 0
[zk: localhost:2181(CONNECTED) 25] getAcl /node2
'ip,'192.168.60.129
: cdrwa
#使用IP非 192.168.60.129 的机器
[zk: localhost:2181(CONNECTED) 0] get /node2
Authentication is not valid : /node2 #没有权限
Auth授权模式:(密码是明文形式)
addauth digest<user>:<password> : #添加认证用户
setAcl <path> auth:<user>:<acl>
[zk: localhost:2181(CONNECTED) 2] create /node3 "node3"
Created /node3
#添加认证用户
[zk: localhost:2181(CONNECTED) 4] addauth digest itcast:123456
[zk: localhost:2181(CONNECTED) 1] setAcl /node3 auth:itcast:cdrwa
cZxid = 0x15
ctime = Fri Dec 13 22:41:04 CST 2019
mZxid = 0x15
mtime = Fri Dec 13 22:41:04 CST 2019
pZxid = 0x15
cversion = 0
dataVersion = 0
aclVersion = 1
ephemeralOwner = 0x0
dataLength = 5
numChildren = 0
[zk: localhost:2181(CONNECTED) 0] getAcl /node3
'digest,'itcast:673OfZhUE8JEFMcu0l64qI8e5ek=
: cdrwa
#添加认证用户后可以访问
[zk: localhost:2181(CONNECTED) 3] get /node3
node3
cZxid = 0x15
ctime = Fri Dec 13 22:41:04 CST 2019
mZxid = 0x15
mtime = Fri Dec 13 22:41:04 CST 2019
pZxid = 0x15
cversion = 0
dataVersion = 0
aclVersion = 1
ephemeralOwner = 0x0
dataLength = 5
numChildren = 0
Digest授权模式:(密码需要加密)
setAcl <path> digest:<user>:<password>:<acl>
这里的密码是经过SHA1及BASE64处理的密文,在SHELL中可以通过以下命令计算 :
//echo -n <user>:<password> | openssl dgst -binary -sha1 | openssl base64
echo -n root1:123456 | openssl dgst -binary -sha1 | openssl base64
[zk: localhost:2181(CONNECTED) 4] create /node4 "node4"
Created /node4
#使用是上面算好的密文密码添加权限:
[zk: localhost:2181(CONNECTED) 5] setAcl /node4
digest:root1:qlzQzCLKhBROghkooLvb+Mlwv4A=:cdrwa
cZxid = 0x1c
ctime = Fri Dec 13 22:52:21 CST 2019
mZxid = 0x1c
mtime = Fri Dec 13 22:52:21 CST 2019
pZxid = 0x1c
cversion = 0
dataVersion = 0
aclVersion = 1
ephemeralOwner = 0x0
dataLength = 5
numChildren = 0
[zk: localhost:2181(CONNECTED) 6] getAcl /node4
'digest,'root1:qlzQzCLKhBROghkooLvb+Mlwv4A=
: cdrwa
[zk: localhost:2181(CONNECTED) 3] get /node4
Authentication is not valid : /node4 #没有权限
[zk: localhost:2181(CONNECTED) 4] addauth digest root1:123456 #添加
认证用户
[zk: localhost:2181(CONNECTED) 5] get /node4
1 #成功读取数据
cZxid = 0x1c
ctime = Fri Dec 13 22:52:21 CST 2019
mZxid = 0x1c
mtime = Fri Dec 13 22:52:21 CST 2019
pZxid = 0x1c
cversion = 0
dataVersion = 0
aclVersion = 1
ephemeralOwner = 0x0
dataLength = 5
numChildren = 0
多种模式授权:
[zk: localhost:2181(CONNECTED) 0] create /node5 "node5"
Created /node5
[zk: localhost:2181(CONNECTED) 1] addauth digest itcast:123456 #添加认
证用户
[zk: localhost:2181(CONNECTED) 2] setAcl /node5
ip:192.168.60.129:cdra,auth:itcast:cdrwa,digest:root1:qlzQzCLKhBROgh
kooLvb+Mlwv4A=:cdrwa
acl 超级管理员
zookeeper的权限管理模式有一种叫做super,该模式提供一个超管可以方便的访问 任何权限的节点
echo -n super:admin | openssl dgst -binary -sha1 | openssl base64
打开zookeeper目录下的/bin/zkServer.sh服务器脚本文件,找到如下一行:
nohup $JAVA "-Dzookeeper.log.dir=${ZOO_LOG_DIR}"
"Dzookeeper.root.logger=${ZOO_LOG4J_PROP}"
添加一下信息:
"Dzookeeper.DigestAuthenticationProvider.superDigest=super:xQJmxLMiHGwaqBv
st5y6rkB6HQs="
最终变成:
nohup $JAVA "-Dzookeeper.log.dir=${ZOO_LOG_DIR}" "
Dzookeeper.root.logger=${ZOO_LOG4J_PROP}" "
Dzookeeper.DigestAuthenticationProvider.superDigest=super:xQJmxLMiHGwaqBv
st5y6rkB6HQs="\-cp "$CLASSPATH" $JVMFLAGS $ZOOMAIN "$ZOOCFG" > "$_ZOO_DAEMON_OUT"
2>&1 < /dev/null &
启动zookeeper ,输入一下添加权限
addauth digest super:admin #添加认证用户