avatar

目录
ZooKeeper专题(2):zk的客户端命令行

第4章 ZK基本特性与基于Linux的ZK客户端命令行学习

4-1 zookeeper常用命令行操作

  • ls与ls2

  • get与stat

bash
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
# 打开客户端
$ zkCli.sh

# ls与ls2命令
# - ls path [watch] ls2 path [watch]

## ls:查看节点
[zk: localhost:2181(CONNECTED) 1] ls /
[zookeeper]
[zk: localhost:2181(CONNECTED) 2] ls /zookeeper
[quota]
[zk: localhost:2181(CONNECTED) 3]

## ls2:相当于ls+stat命令 的整合
[zk: localhost:2181(CONNECTED) 3] ls2 /
[zookeeper]
# - 以下是一些状态信息
cZxid = 0x0
ctime = Thu Jan 01 08:00:00 CST 1970
mZxid = 0x0
mtime = Thu Jan 01 08:00:00 CST 1970
pZxid = 0x0
cversion = -1
dataVersion = 0
aclVersion = 0
ephemeralOwner = 0x0
dataLength = 0
numChildren = 1

# get与stat命令
## stat(查看状态)
[zk: localhost:2181(CONNECTED) 4] stat /
cZxid = 0x0
ctime = Thu Jan 01 08:00:00 CST 1970
mZxid = 0x0
mtime = Thu Jan 01 08:00:00 CST 1970
pZxid = 0x0
cversion = -1
dataVersion = 0
aclVersion = 0
ephemeralOwner = 0x0
dataLength = 0
numChildren = 1

## get:查看节点数据
[zk: localhost:2181(CONNECTED) 5] get /
# 次数该节点数据为null
cZxid = 0x0 # zk为该节点分配的id
ctime = Thu Jan 01 08:00:00 CST 1970 # 节点创建的时间
mZxid = 0x0 # 修改后的zk分配的id ??
mtime = Thu Jan 01 08:00:00 CST 1970 # 节点修改的时间
pZxid = 0x0 # 子节点id
cversion = -1 # 子节点的版本号
dataVersion = 0 # 当前节点数据的版本号(数据修改,版本号会累加1)
aclVersion = 0 # 权限版本号(后面讲)
ephemeralOwner = 0x0 # (后面讲)
dataLength = 0 # 数据长度
numChildren = 1 # 子节点个数

4-2 session的基本原理与create命令的使用

session的基本原理

  • 客户端与服务端之间的连接,称为 会话(session)
  • 每个session都可以设置超时时间(超时session就销毁了)
  • 心跳结束(存活指令),session过期
  • session过期,则临时节点znode会被抛弃
  • 心跳机制:客户端向服务端的ping包请求(表示客户端存活)

create命令

  • 创建 非顺序、持久化 节点
  • 创建/删除 临时节点。
  • 创建顺序节点
bash
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
# create命令
# - 语法 :create [-s] [-e] path data acl

# ------------------------------------------------

## 创建非顺序、持久化 节点
[zk: localhost:2181(CONNECTED) 9] create /newnode node-data
Created /newnode
[zk: localhost:2181(CONNECTED) 10] get /newnode
node-data # 节点数据
cversion = 0
...
dataLength = 9 # 数据长度变了(“node-data”的长度)
numChildren = 0

# ------------------------------------------------

## 创建临时节点
[zk: localhost:2181(CONNECTED) 11] create -e /newnode/tmp tmp-data
Created /newnode/tmp

[zk: localhost:2181(CONNECTED) 13] get /newnode
node-data
...
cversion = 1 # 子节点做了一次更新,版本号+1
dataVersion = 0
aclVersion = 0
ephemeralOwner = 0x0
dataLength = 9
numChildren = 1

[zk: localhost:2181(CONNECTED) 12] get /newnode/tmp
tmp-data
...
ephemeralOwner = 0x1000d1f9e2a0001 # 0x0代表持久节点,否则就是临时节点
dataLength = 8
numChildren = 0


## 删除临时节点
# - 心跳机制,客户端挂了,临时节点自动删除
control+c 断开连接后重新连进去
[zk: localhost:2181(CONNECTED) 0] ls /newnode
[tmp] # 为什么还存在?: 时效性,session断开之后还有一段时间心跳机制。心跳断开之后才会删除tmp

# - 再次尝试
[zk: localhost:2181(CONNECTED) 1] ls /newnode
[] # tmp节点消失了

# ------------------------------------------------

# 创建顺序节点
[zk: localhost:2181(CONNECTED) 2] create -s /newnode/sec seq-data
Created /newnode/sec0000000001 # 重命名了
[zk: localhost:2181(CONNECTED) 3] create -s /newnode/sec seq-data
Created /newnode/sec0000000002 # 文件名的编号累加
[zk: localhost:2181(CONNECTED) 4] ls /newnode
[sec0000000001, sec0000000002] # 产生了2个顺序节点

4-3 set与delete命令的使用

  • set
    • 直接值覆盖
    • 使用版本号更新数据
bash
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
# set:修改节点
# - 语法:set path data [version]
[zk: localhost:2181(CONNECTED) 6] get /newnode
node-data
...
dataVersion = 0 # 数据还没被修改
...
# - 修改节点值
[zk: localhost:2181(CONNECTED) 7] set /newnode node-data1
...
[zk: localhost:2181(CONNECTED) 8] get /newnode
node-data1 # 值变化
...
dataVersion = 1 # 数据版本号+1
...

# 修改指定版本号的数据(乐观锁的使用方式)
[zk: localhost:2181(CONNECTED) 9] set /newnode node-data2 1
...
dataVersion = 2
...

# - 再次修改相同版本号
[zk: localhost:2181(CONNECTED) 10] set /newnode node-data2 1
version No is not valid : /newnode # 非法操作
# - 必须使用最新的版本号更新
[zk: localhost:2181(CONNECTED) 11] set /newnode node-data3 2
...
dataVersion = 3
...
  • delete
    • 直接删除节点
    • 使用版本号删除
bash
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
# delete:删除节点
# - 语法:delete path [version]
# 如果指定版本号,会删除对应版本号;不指定的话,直接删除该节点

## 直接删除节点
[zk: localhost:2181(CONNECTED) 13] ls /newnode
[sec0000000001, sec0000000002]
[zk: localhost:2181(CONNECTED) 14] delete /newnode/sec0000000001
[zk: localhost:2181(CONNECTED) 15] ls /newnode
[sec0000000002]

## 删除指定版本号
[zk: localhost:2181(CONNECTED) 16] set /newnode/sec0000000002 data-111
...
dataVersion = 1 # 更新了版本号
...

## - 尝试删除旧版本号
[zk: localhost:2181(CONNECTED) 17] delete /newnode/sec0000000002 0
version No is not valid : /newnode/sec0000000002 # 非法,必须要最新的

## - 删除最新的版本号
[zk: localhost:2181(CONNECTED) 18] delete /newnode/sec0000000002 1
[zk: localhost:2181(CONNECTED) 19] ls /newnode
[] # 没数据了
  • 结论:使用set和delete命令时尽量带版本号,这样可以做到乐观锁的效果(能够保证并发下的数据安全,因为不支持操作旧版本号)

4-4 zk特性 – 理解watcher机制

watcher机制的理解

  • 针对每个节点的操作,都会有一个监督者 -> wathcer

  • 当监控的某个对象(znode)发生变化(增删改),就会触发wathcer事件(理解为触发器)

  • zk的watcher是一次性的,触发后立即销毁(有的第三方客户端工具可以设置为永久性)

  • a节点的父、子节点的 增删改 都能触发a节点的wathcer

  • 针对不同操作,触发的watcher事件也不同:

    1. (子)节点创建事件
    2. (子)节点删除事件
    3. (子)节点数据变化事件

4-5 父节点watcher事件

通过get path [watch] 设置watcher (或者用ls、set)

get、stat 设置父节点触发

wathcer事件类型

  • 创建父节点触发(当没有节点时去创建普通节点):NodeCreated
  • 修改父节点数据触发:NodeDataChanged
  • 删除父节点触发:NodeDelete
bash
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
# NodeCreated
# - 语法:stat path [watch]、get path [watch]

# 给node2绑定一个watch
$ stat /node2 watch
Node does not exist: /node2 # 节点还没创建,但是watch会监听node2
$ create /node2 data-1

WATCHER::
# - NodeCreated事件被触发了
WatchedEvent state:SyncConnected type:NodeCreated path:/node2
Created /node2

# ------------------------------------------

# NodeDataChanged
$ set /node2 data-2
... # 并没有触发,因为没绑定(watch一旦触发就会被销毁)
# - 先绑定
$ get /node2 watch
...
# 再修改节点值
$ set /node2 data-3

WATCHER::
cZxid = 0x12
# - NodeDataChanged事件被触发了
WatchedEvent state:SyncConnected type:NodeDataChanged path:/node2
...

# ------------------------------------------

# NodeDelete
$ get /node2 watch
...
$ delete /node2

WATCHER::
# - NodeDeleted事件被触发了
WatchedEvent state:SyncConnected type:NodeDeleted path:/node2

4-6 子节点watcher事件

ls设置子节点触发

wathcer事件类型

  • ls为父节点设置watcher,创建子节点触发:NodeChildrenChanged
  • ls为父节点设置watcher,删除子节点触发:NodeChildrenChanged
  • ls为父节点设置watcher,修改子节点不触发事件
bash
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
# 创建子节点触发:NodeChildrenChanged
$ ls /newnode watch
[]
$ create /newnode/abc data-1

WATCHER::
# - 触发NodeChildrenChanged
WatchedEvent state:SyncConnected type:NodeChildrenChanged path:/newnode
Created /newnode/abc


# 删除子节点触发:NodeChildrenChanged
$ ls /newnode watch
[abc]
$ delete /newnode/abc

WATCHER::

WatchedEvent state:SyncConnected type:NodeChildrenChanged path:/newnode


# 修改子节点不触发事件
$ ls /newnode watch
[aaa]
$ set /newnode/aaa data-2
# - 没有事件触发,可以使用get绑定去触发NodeDataChanged
...

4-7 watcher常用使用场景

  • 统一资源配置

    分布式下主从节点的信息保持一致性

4-8 权限acl详解,acl的构成-scheme与id

ACL(access control lists)权限控制

  • 针对节点可以设置相关读写等权限,目的为了保障数据安全性
  • 权限permissions可以指定不同的权限范围以及角色(类比shiro)

ACL命令行

  • getAcl:获取某个节点的acl权限信息
  • setAcl:设置某个节点的acl权限信息
  • addauth:输入认证授权信息,注册时输入明文密码(登录),在zk里密码以加密形式存储

ACL的构成

  • zk的acl通过 [scheme: id: permissions] 来构成权限列表

    scheme:代表采用的某种权限机制

    id:代表允许访问的用户

    permissions:权限组合字符串

scheme的类型

  • world:world下只有一个id,即只有一个用户,该用户是anyone。

    写法为 world:anyone:[permissions]

  • auth:代表认证登录,需要注册用户有权限就可以,

    写法为 auth:user:password:[permissions]

  • digest:类似auth,但需要对密码加密才能访问(多采用digest而不是auth )

    写法为 digest:username:BASE64(SHA1(password)):[permissions]

  • ip:限制ip进行访问

    写法为 ip:192.168.1.1:[permissions]

  • super:代表超级管理员,拥有所有的权限(运维、总监)

bash
1
2
3
4
# - 语法:setAcl path acl 、getAcl path、addauth scheme auth
$ getAcl /newnode/aaa
'world,'anyone # 创建节点的默认权限
: cdrwa

4-9 acl的构成-permissions

权限字符串缩写 crdwa

  • create: 创建子节点

  • read: 获取节点/子节点

  • write: 设置节点数据

  • delete: 删除子节点

  • admin: 设置权限

4-10 acl命令行world

  • world:anyone:cdrwa
bash
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# 默认权限
$ getAcl /newnode/aaa
'world,'anyone
: cdrwa
# 设置权限
$ setAcl /newnode/aaa world:anyone:crwa
...
$ getAcl /newnode/aaa
'world,'anyone
: crwa # 少了删除子节点权限d
$ create /newnode/aaa/xyz data-1
Created /newnode/aaa/xyz
$ delete /newnode/aaa/xyz
# 删除失败
Authentication is not valid : /newnode/aaa/xyz

4-11 acl命令行auth

  • auth:user:password:[permissions]
bash
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
$ getAcl /names/aaa
'world,'anyone
: cdrwa
$ setAcl /names/aaa auth:mxx:123:cdrwa # mxx:123 用户名:密码
# 非法,需要先登录再使用
Acl is not valid : /names/aaa
# 登录用户
$ addauth digest mxx:123
$ setAcl /names/aaa auth:mxx:123:cdrwa
...
$ getAcl /names/aaa
# 权限设置成功,密码以密文形式存储
'digest,'mxx:qSAyqpu3y3SZ5+XU1kLf17wYN9o=
: cdrwa

# 如果重新设置用户名
$ setAcl /names/aaa auth:mmm:456:cdrwa
...
$ getAcl /names/aaa
# 会发现无效,用户名和密码都是第一次的值
'digest,'mxx:qSAyqpu3y3SZ5+XU1kLf17wYN9o=
: cdrwa
# 此写法代表使用默认用户名(第一次设置的那个)进行权限操作
$ setAcl /names/aaa auth::cdrwa
...
$ getAcl /names/aaa
'digest,'mxx:qSAyqpu3y3SZ5+XU1kLf17wYN9o=
: cdrwa

4-12 acl命令行digest

  • digest:username:BASE64(SHA1(password)):[permissions]
bash
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
# 先control+c 退出当前用户(mxx的用户),重新进入
$ create /names/bbb d-bbb
Created /names/bbb
$ getAcl /names/bbb
'world,'anyone
: cdrwa
# digest使用密文形式
$ setAcl /names/bbb digest:mxx:qSAyqpu3y3SZ5+XU1kLf17wYN9o=:cdra
...
$ getAcl /names/bbb
'digest,'mxx:qSAyqpu3y3SZ5+XU1kLf17wYN9o=
: cdra

# 区别:使用auth写的明文,保存的密文;使用digest使用密文,保存密文。

$ get /names/bbb
# 非法,因为没登录
Authentication is not valid : /names/bbb
# 登录,不管auth和digest 登录密码都用铭文
$ addauth digest mxx:123
$ get /names/bbb
... # 登录成功后就能获取数据了
$ set /names/bbb 111
# 没有w权限
Authentication is not valid : /names/bbb

4-13 acl命令行ip

  • ip:192.168.1.1:[permissions]
bash
1
2
3
4
5
6
7
8
9
10
11
12
13
14
$ create /names/ccc d-ccc
Created /names/ccc
$ getAcl /names/ccc
'world,'anyone
: cdrwa
# ip是mac的ip
$ setAcl /names/ccc ip:10.6.104.28:cdwra
...
$ getAcl /names/ccc
'ip,'10.6.104.28
: cdrwa
$ get /names/ccc
# 非法,只有特定ip可以进行操作(之后通过java代码讲)
Authentication is not valid : /names/ccc

4-14 acl之super超级管理员

超级权限可以增删改查任何节点

  • Super

    1、修改zkServer.sh 增加super管理员

    2、重启zkServer.sh

1、配置

bash
1
2
3
$ vim zkServer.sh
# 搜索nohup
# 添加"-Dzookeeper.DigestAuthenticationProvider.superDigest=mxx:qSAyqpu3y3SZ5+XU1kLf17wYN9o="到图中位置

![屏幕快照 2018-11-14 上午10.47.29](20181112164832926/屏幕快照 2018-11-14 上午10.47.29.png)

2、使用

bash
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# 重启
$ zkServer.sh restart
$ zkCli.sh
$ ls /names/ccc
# 没有权限
Authentication is not valid : /names/ccc
$ getAcl /names/ccc
'ip,'10.6.104.28
: cdrwa
# 登录超级管理员
$ addauth digest mxx:123
# 有权限了
$ ls /names/ccc
[]
$ get /names/ccc
...

4-15 acl的常用使用场景

  • 开发/测试环境分离,开发者无权操作测试库的节点,只能看。

    (命名空间)

  • 生产环境上控制指定ip的服务可以访问相关节点,防止混乱。

    弊端:客户端是动态ip

4-16 zk四字命令

Four Letter Words

  • zk可以通过它自身提供的简写命名和服务器进行交互
  • 需要使用到nc命令,安装:yum install nc
  • echo [commond] | nc [ip] [port]

命令参考官方文档:http://zookeeper.apache.org/doc/r3.4.13/zookeeperAdmin.html#sc_zkCommands

stat、ruok、dump、conf、cons、envi

mntr、 wchs(wchc、wchp)

bash
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
$ sudo yum install nc

# [stat] 查看zk的状态信息,以及mode
$ echo stat | nc 10.211.55.6 2181
Zookeeper version: 3.4.11-37e277162d567b55a07d1755f0b31c32e93c01a0, built on 11/01/2017 18:06 GMT
Clients:
/10.211.55.6:59494[0](queued=0,recved=1,sent=0)
...
Mode: standalone # 单机运行
Node count: 10

# [ruok] 查看zkserver是否启动,返回imok
$ echo ruok | nc 10.211.55.6 2181
imok

# [dump] 列出未经处理的会话和临时节点
# - 登录cli创建临时节点
$ create -e /names/tmp-dump 111
# - 返回命令行使用如下命令
$ echo dump | nc localhost 2181
SessionTracker dump:
Session Sets (3):
0 expire at Thu Jan 01 17:19:10 CST 1970:
0 expire at Thu Jan 01 17:19:20 CST 1970:
1 expire at Thu Jan 01 17:19:30 CST 1970:
0x10001e1e9430001 # session的id
ephemeral nodes dump:
Sessions with Ephemerals (1):
0x10001e1e9430001:
/names/tmp-dump # 列出临时节点的目录

# [conf] 查看服务器配置
$ echo conf | nc localhost 2181
clientPort=2181
dataDir=/usr/local/zookeeper/dataDir/version-2
dataLogDir=/usr/local/zookeeper/dataLogDir/version-2
tickTime=2000
maxClientCnxns=60
minSessionTimeout=4000
maxSessionTimeout=40000
serverId=0

# [cons]展示连接到服务器的客户端信息
$ echo cons | nc localhost 2181
/0:0:0:0:0:0:0:1:52368[0](queued=0,recved=1,sent=0)
/127.0.0.1:51142[1](queued=0,recved=55,sent=55,sid=0x10001e1e9430001,lop=PING,est=1542165605721,to=30000,lcxid=0x7,lzxid=0x35,lresp=33829115,llat=0,minlat=0,avglat=0,maxlat=3)

# [envi] 环境变量
$ echo envi | nc localhost 2181
zookeeper.version=...
host.name=machinehost
java.version=1.8.0_161
...

# [mntr] 监控zk健康信息
$ echo mntr | nc localhost 2181
...
zk_znode_count 11 # 节点数
zk_watch_count 0 # 监控节点数
zk_ephemerals_count 1 # 临时节点

# [wchs] 展示watch的信息
# - 进入cli设置watch,设置2个watch
$ get /names watch
...
$ get /newnode watch
...
$ echo wchs | nc localhost 2181
1 connections watching 2 paths
Total watches:2

# [wchc]、[wchp] session与watch 及 path与watch
# - zk默认没有开启这两个命令
$ echo wchc | nc localhost 2181
wchc is not executed because it is not in the whitelist.
$ echo wchp | nc localhost 2181
wchp is not executed because it is not in the whitelist.
# - 配置参见官网whitelist
$ cd conf
$ vim zoo.cfg
# 在最后一行添加如下命令
# ----------vim start-------------
# 允许所有四字命令
4lw.commands.whitelist=*

# ----------vim end-------------
$ zkServer.sh restart
$ echo wchc | nc localhost 2181
0x10001e1e9430001 # 该session创建了2个带watch的节点
/names
/newnode
$ echo wchp | nc localhost 2181
# - 当前带watch的path是由哪个session创建的
/names
0x10001e1e9430001
/newnode
0x10001e1e9430001
文章作者: Machine
文章链接: https://machine4869.gitee.io/2018/11/12/20181112164832926/
版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 哑舍
打赏
  • 微信
    微信
  • 支付宝
    支付宝

评论