本文是基于 zookeeper 3.6.1 编写的


节点操作

新增节点

  1. create [-s] [-e] path [data] # 其中-s表示有序节点,-e表示临时节点

通过该条命令,可以简单将节点划分为四种:

  • 持久+有序节点
  • 持久+无序节点
  • 临时+有序节点
  • 临时+无序节点

    持久节点

    默认 create 就是创建的持久化节点:
    1. create /a "111"
    如果要创建持久化+有序节点,那么加上 -s 即可:
    1. [zk: localhost:2181(CONNECTED) 2] create -s /a "aaaa"
    2. Created /a0000000003

作用:可以用持久化有序节点,为分布式系统提供唯一的ID。

临时节点

临时节点的生命周期取决于 **session** ,如果session结束了,那么临时节点的一生就结束了。

如果要创建临时节点,需要加上参数 -e

[zk: localhost:2181(CONNECTED) 5] create -e /ea "eaaaaaaaaaa"
Created /ea

如果要创建临时+有序节点,需要额外加上参数 -s

[zk: localhost:2181(CONNECTED) 6] create -e -s /eb "ebbbbbbbbbbbbbbb"
Created /eb0000000005

临时有序节点在session结束以后虽然会被销毁,但是它的序号并不会跟着消失,而是继续往后递增。

作用:可以用临时有序节点,为分布式系统提供锁机制。

更新节点

更新命令为:

set [-s] [-v <version>] <PATH> <DATA>
  • ,指定节点
  • ,要修改的数据
  • [-v version],类似mysql的乐观锁的版本号;如果版本对应上了,那么久执行更新,如果没对上就拒绝更新。优点类似于CAS操作。
  • [-s],更新完后输出更新后的状态

更新前:

[zk: localhost:2181(CONNECTED) 2] get -s /zoo01
123
cZxid = 0x9
ctime = Fri Jun 26 03:04:26 EDT 2020
mZxid = 0x9
mtime = Fri Jun 26 03:04:26 EDT 2020
pZxid = 0x9
cversion = 0
dataVersion = 0
aclVersion = 0
ephemeralOwner = 0x0
dataLength = 3
numChildren = 0

更新后:

[zk: localhost:2181(CONNECTED) 3] set /zoo01 "1111111111111"
[zk: localhost:2181(CONNECTED) 4] get -s /zoo01
1111111111111
cZxid = 0x9
ctime = Fri Jun 26 03:04:26 EDT 2020
mZxid = 0x18
mtime = Fri Jun 26 04:34:26 EDT 2020
pZxid = 0x9
cversion = 0
dataVersion = 1
aclVersion = 0
ephemeralOwner = 0x0
dataLength = 13
numChildren = 0

如果加上版本号限制,那么只有给定的 version 匹配上了该节点的 dataVersion ,才允许更新,接着上面

[zk: localhost:2181(CONNECTED) 19] set /zoo01 "9999" -v 9
version No is not valid : /zoo01

删除节点

命令格式:

delete [-v <version>] <PATH>

删除 /zoo01 节点:

[zk: localhost:2181(CONNECTED) 21] delete -v 9 /zoo01
version No is not valid : /zoo01
[zk: localhost:2181(CONNECTED) 22] delete -v 6 /zoo01
[zk: localhost:2181(CONNECTED) 23]

注意,该命令删除的节点一定不能有子节点(即使子节点是null)!!!

[zk: localhost:2181(CONNECTED) 23] create /zoo01
Created /zoo01
[zk: localhost:2181(CONNECTED) 24] create /zoo01/node01
Created /zoo01/node01
[zk: localhost:2181(CONNECTED) 25] delete /zoo01
Node not empty: /zoo01

旧版本:如果想要删除有子节点的节点,需要使用命令 rmr <PATH>
新版本:使用命令 deleteall <PATH> ,删除某个路径及其子节点, deleteall <PATH> 的格式如下所示:

deleteall path [-b batch size]
  • [-b batch size],目前没弄懂干嘛用的;个人猜测是用来提高吞吐量的

查看节点

查看命令格式如下所示:

get [-s] [-w] path
  • [-s] ,获取数据的同时打印该节点的状态
  • [-w] ,获取数据的同时,给这个节点加上一个 WATCH 。使用该参数的时候,需要启用 printwatches 。直接输入 printwatches on 即可
    [zk: localhost:2181(CONNECTED) 30] printwatches
    printwatches is on
    
状态属性 说明
cZxid 该节点创建时的事务ID
ctime 该节点创建时的时间
mZxid 该节点最后一次修改的事务ID
mtime 该节点最近一次的更新时间
pZxid 该节点内最后一次新增、删除子节点(直接子节点,子节点的子节点不算)时的事务ID
cversion 子节点(直接子节点,子节点的子节点不算)的新增、删除次数。子节点的内容变更不算
dataVersion 该节点的数据更改次数
aclVersion 结点的ACL(权限)更改次数——类似linux的权限列表,维护的是当前结点的权限列表被修改的次数
ephemeralOwner 如果结点是临时结点,则表示创建该结点的会话的SessionID;如果是持久结点,该属性值为0
dataLength 该节点的数据长度
numChildren 该节点的子节点个数

查看节点

使用 get [-w] [-s] PATH ,其中:

  • [-w] ,表示监听
  • [-s] ,表示顺带着输出节点状态信息 ```bash [zk: localhost:2181(CONNECTED) 77] get -w /zoo01 2 [zk: localhost:2181(CONNECTED) 78] set /zoo01 “11” WATCHER::

WatchedEvent state:SyncConnected type:NodeDataChanged path:/zoo01


<a name="2srkj"></a>
### 查看节点状态
使用 `stat [-w] PATH` 查看节点状态,相当于使用 `get -s PATH` 。其中的 `[-w]` 表示监听该节点的状态修改情况。注意是 `type:NodeDataChanged` 
```bash
[zk: localhost:2181(CONNECTED) 68] stat -w /zoo01
cZxid = 0x26
ctime = Fri Jun 26 05:13:28 EDT 2020
mZxid = 0x26
mtime = Fri Jun 26 05:13:28 EDT 2020
pZxid = 0x2c
cversion = 3
dataVersion = 0
aclVersion = 0
ephemeralOwner = 0x0
dataLength = 0
numChildren = 1
[zk: localhost:2181(CONNECTED) 70] set /zoo01 "1111"
WATCHER::

WatchedEvent state:SyncConnected type:NodeDataChanged path:/zoo01

查看节点列表

旧版本:使用 ls PATH ,查看某个路径下的所有子节点
新版本:使用 ls [-s] [-w] [-R] PATH ,查看某个路径下的所有子节点,其中:

  • -s ,表示顺带着显示子节点元数据信息
  • -w ,监视子节点的增、删信息,注意!是增、删信息,修改数据的内容不管(注意 type:NodeChildrenChanged ) ```bash [zk: localhost:2181(CONNECTED) 72] ls -w /zoo01 [a] [zk: localhost:2181(CONNECTED) 73] set /zoo01/a “222” # 给ls加上watch,修改子节点的内容不触发watch [zk: localhost:2181(CONNECTED) 74] create /zoo01/b “33” # 只有创建/删除节点才会触发watch WATCHER::

WatchedEvent state:SyncConnected type:NodeChildrenChanged path:/zoo01


- `-R` ,递归显示子节点的子节点(不能和 `-s` 一起使用,一起使用的话仅 `-R` 生效)
```bash
[zk: localhost:2181(CONNECTED) 65] ls -s /zoo01
[a]
cZxid = 0x26
ctime = Fri Jun 26 05:13:28 EDT 2020
mZxid = 0x26
mtime = Fri Jun 26 05:13:28 EDT 2020
pZxid = 0x2c
cversion = 3
dataVersion = 0
aclVersion = 0
ephemeralOwner = 0x0
dataLength = 0
numChildren = 1
[zk: localhost:2181(CONNECTED) 64] ls -s -R /zoo01
/zoo01
/zoo01/a
/zoo01/a/b

ACL权限控制

zookeeper里面权限分为三个部分:

  • 授权模式(scheme)
    • world,只有一个用户,就是anyone(root?)
    • ip,对客户端使用IP进行认证
    • auth,使用已添加认证用户进行认证。提供用户名密码时,密码是明文的
    • digest,用户名+密码认证。提供的是加密的
  • 授权对象(id)
    • world的授权对象是anyone用户
    • ip的授权对象是某个具体的ip地址
    • auth和digest是具体要认证的某个用户
  • 权限(permission)
    • create( c ),在当前节点下创建子节点的权限
    • delete( d ),在当前节点删除子节点(仅直接节点,子节点的子节点无效)的权限
    • read( r ),读取节点数量及显示子节点列表的权限
    • write( w ),设置节点数据的权限
    • admin( a ),可以设置节点访问控制列表权限(就是管理权限的权限)
命令 使用示例 描述
getAcl [-s] PATH getAcl /zoo01 查看某个节点的ACL权限
setAcl [-s] [-v <version>] PATH SCHEME:ID:ACL setAcl /zoo world:anyone:craw 设置ACL权限
addauth addauth 添加认证用户

world模式实战

移除 /zoo 节点的的create权限,那么就不能在该节点下创建直接子节点

[zk: localhost:2181(CONNECTED) 88] getAcl /zoo/a
'world,'anyone
: cdrwa
[zk: localhost:2181(CONNECTED) 89] setAcl /zoo world:anyone:drwa
[zk: localhost:2181(CONNECTED) 90] create /zoo/c
Authentication is not valid : /zoo/c
[zk: localhost:2181(CONNECTED) 91] create /zoo/a/aa
Created /zoo/a/aa
[zk: localhost:2181(CONNECTED) 92]

注意, stat 命令不受任何权限影响,都能执行(以前的版本 getAcl 也不受权限的影响,新版本会受 read 权限的影响了)

ip模式

授权对象一旦指向了别人,自己就没的办法了。
本地localhost,给 192.168.136.134 设置了ip模式的 crw 权限:

[zk: localhost:2181(CONNECTED) 2] getAcl /keeper
'world,'anyone
: cdrwa
[zk: localhost:2181(CONNECTED) 3] setAcl /keeper ip:192.168.136.134:crw
[zk: localhost:2181(CONNECTED) 4] getAcl /keeper
Authentication is not valid : /keeper
[zk: localhost:2181(CONNECTED) 5]

192.168.136.134的主机:

[zk: 192.168.136.128(CONNECTED) 1] getAcl /keeper
'ip,'192.168.136.134
: crw
[zk: 192.168.136.128(CONNECTED) 2] create /keeper/a
Created /keeper/a
[zk: 192.168.136.128(CONNECTED) 3] delete /keeper/a              # 删除就失败了
Authentication is not valid : /keeper/a
[zk: 192.168.136.128(CONNECTED) 4]

PS:要想同时指定多个IP地址,需要这样做:

setAcl /keeper ip:192.168.136.134:crw,ip:192.168.136.128:crwa

auth模式(许多教程是错的!)

  1. 首先要添加用户 addauth digest <user>:<password>
  2. 再给某个路径设置权限 setAcl <path> auth::<acl> (这里 acl 千万别设错了!我当时没注意,填了6个1,然后这个节点啥权限都没了) ```bash [zk: localhost:2181(CONNECTED) 9] create /zoo1 Created /zoo1 [zk: localhost:2181(CONNECTED) 10] getAcl /zoo1 ‘world,’anyone : cdrwa [zk: localhost:2181(CONNECTED) 11] setAcl /zoo1 auth:codeleven:cawd [zk: localhost:2181(CONNECTED) 12] getAcl /zoo1 ‘digest,’codeleven:Uj2OCeg9/wrj8mQJU6iGZj/IEcM= : cdwa [zk: localhost:2181(CONNECTED) 13] quit # 退出重进

[zk: localhost:2181(CONNECTED) 0] getAcl /zoo1 # 一开始进来没有任何权限,因为还没认证 Authentication is not valid : /zoo1 [zk: localhost:2181(CONNECTED) 1] addauth digest codeleven:123 # 认证一个错误的密码,正确的是111111 [zk: localhost:2181(CONNECTED) 2] getAcl /zoo1 # 密码错误不会提示,只是无法访问 Authentication is not valid : /zoo1 [zk: localhost:2181(CONNECTED) 3] addauth digest codeleven:111111 # 输入正确的密码 [zk: localhost:2181(CONNECTED) 4] getAcl /zoo1 # 能正常访问 ‘digest,’codeleven:Uj2OCeg9/wrj8mQJU6iGZj/IEcM= : cdwa

注意,**对于auth模式来说, `setAcl` 是不需要指定 `id` 参数的**,Zookeeper会拿**当前已经 `addauth` 的用户作为授权对象**(如果有多个已经 `addauth` 的用户,就创建多个用户的权限):
```bash
[zk: localhost:2181(CONNECTED) 0] create /node 'node'
Created /node
[zk: localhost:2181(CONNECTED) 1] setAcl /node auth:gg:cdraw
Acl is not valid : /node                                      # auth模式下的setAcl,需要先addauth
[zk: localhost:2181(CONNECTED) 2] addauth digest 1:1          # 执行第一遍addauth
[zk: localhost:2181(CONNECTED) 3] setAcl /node auth:gg:cdraw  # auth模式下的id字段必须要预留
                                                              # 但是内容随意,我给了gg,但是设置的对象是1 
[zk: localhost:2181(CONNECTED) 4] getAcl /node                  
'digest,'1:PndHMdM9kiSsNq89hbofgbMbyE0=
: cdrwa
[zk: localhost:2181(CONNECTED) 5] addauth digest 2:2          # 又addauth了2和3
[zk: localhost:2181(CONNECTED) 6] addauth digest 3:3        
[zk: localhost:2181(CONNECTED) 7] setAcl /node auth:mm:cdraw  # 这里id也随便给了一个,后面正常授权
[zk: localhost:2181(CONNECTED) 8] getAcl /node
'digest,'3:EQ90fWNTGAcPpfuyFvMse+i1+BE=
: cdrwa
'digest,'1:PndHMdM9kiSsNq89hbofgbMbyE0=
: cdrwa
'digest,'2:eiegrg4ZTLQrNWEMges2RKxiJww=
: cdrwa
[zk: localhost:2181(CONNECTED) 9]

Digest授权模式

setAcl <path> digest:<user>:<password>:<acl>

  • 这里的密码需要预先经过 SHA1以及BASE64处理,可以在shell 中通过以下命令计算:
    echo -n <user>:<password> | openssl dgst -binary -sha1 | openssl base64
    
    算出密文后就可以设置给指定的节点:
    # 计算密码
    echo -n codeleven:111111 | openssl dgst -binary -sha1 | openssl base64
    # 获取密码,设置权限列表
    setAcl /hadoop digest:codeleven:Uj2OCeg9/wrj8mQJU6iGZj/IEcM=:cdrwa
    # 现在想要get /hadoop 就需要登录了
    addauth digest codeleven:111111
    get /hadoop
    

auth 和 digest的区别

auth需要添加用户;digest需要计算了密文;
在setAcl这步,auth模式只需要设置用户名即可;digest需要给定用户名+密文
认证这一步,两个人都一样,输入的都是明文

多种授权模式

用逗号隔开多个权限

setAcl PATH SCHEME:CONTENT:ACL[,SCHEME:CONTENT:ACL]
  • CONTENT 指某个模式具体要填写的内容

示例:

setAcl /hadoop digest:codeleven:Uj2OCeg9/wrj8mQJU6iGZj/IEcM=:dra,digest:admin:015uTByzA4zSglcmseJsxTo7n3c=:draw

超级管理员

  1. 首先生成超级管理员的digest,操作和 digest 授权模式一样:

    [root@localhost ~]# echo -n root:root | openssl dgst -binary -sha1 | openssl base64
    qiTlqPLK7XM2ht3HMn02qRpkKIE=
    
  2. 编辑 zkServer.sh 脚本,找到下面这两行:

    158     nohup "$JAVA" $ZOO_DATADIR_AUTOCREATE "-Dzookeeper.log.dir=${ZOO_LOG_DIR}" \
    159     "-Dzookeeper.log.file=${ZOO_LOG_FILE}" "-Dzookeeper.root.logger=${ZOO_LOG4J_PROP}" \
    
  3. 加入一个超级管理配置:

    -Dzookeeper.DigestAuthenticationProvider.superDigest=root:qiTlqPLK7XM2ht3HMn02qRpkKIE=
    
  4. 最后变成这样:

    158     nohup "$JAVA" $ZOO_DATADIR_AUTOCREATE "-Dzookeeper.log.dir=${ZOO_LOG_DIR}" \
    159     "-Dzookeeper.log.file=${ZOO_LOG_FILE}" "-Dzookeeper.root.logger=${ZOO_LOG4J_PROP}" \
    160     "-Dzookeeper.DigestAuthenticationProvider.superDigest=root:qiTlqPLK7XM2ht3HMn02qRpkKIE=" \
    161     -XX:+HeapDumpOnOutOfMemoryError -XX:OnOutOfMemoryError='kill -9 %p' \
    162     -cp "$CLASSPATH" $JVMFLAGS $ZOOMAIN "$ZOOCFG" > "$_ZOO_DAEMON_OUT" 2>&1 < /dev/null &
    
  5. 进入 zkCli.sh 之后,登录超级管理员账号:

    [zk: localhost:2181(CONNECTED) 1] addauth digest root:root
    
  6. 然后就可以无视权限干任何事情了:

    [zk: localhost:2181(CONNECTED) 2] ls /
    [a0000000003, hadoop, keeper, zoo, zookeeper, zookeeper01]
    [zk: localhost:2181(CONNECTED) 4] getAcl /zoo
    'world,'anyone
    :
    [zk: localhost:2181(CONNECTED) 5] deleteall /zoo
    [zk: localhost:2181(CONNECTED) 6] ls /
    [a0000000003, hadoop, keeper, zookeeper, zookeeper01]
    [zk: localhost:2181(CONNECTED) 7]
    

    超级管理员的设置方式和digest授权模式有异曲同工之妙:

  • 超级管理员

    • 对象:服务器
    • 使用 -Dzookeeper.DigestAuthenticationProvider.superDigest=root:qiTlqPLK7XM2ht3HMn02qRpkKIE= 为当前服务器配置权限
  • digest

    • 对象:某一个节点
    • 使用 setAcl 配置权限

版本更新归档

  • 删除指定路径下的所有节点,以前是 rmr ,新版本是 deleteall ,参考官方Issue