摘要:zookeeper概要、zookeeeper环境搭建、zookeeper相关命令、zookeeper节点详解
ZooKeeper是用于分布式应用程序的协调服务。它公开了一组简单的API,分布式应用程序可以基于这些API用于同步,节点状态、配置等信息、服务注册等信息。其由JAVA编写,支持JAVA 和C两种语言的客户端。
一、zookeeper简介
1、zookeeper产生背景
项目从单体到分布式转变之后,将会产生多个节点之间协同的问题。如:
- 每天的定时任务由谁哪个节点来执行?
- RPC调用时的服务发现?
- 如何保证并发请求的幂等
- 如何实现分布式锁等…
这些问题可以统一归纳为多节点协调问题,如果靠节点自身进行协调这是非常不可靠的,性能上也不可取。必须由一个独立的服务做协调工作,它必须可靠,而且保证性能。
2、zookeeper概要
ZooKeeper是用于分布式应用程序的协调服务。它公开了一组简单的API,分布式应用程序可以基于这些API用于同步,节点状态、配置等信息、服务注册等信息。其由JAVA编写,支持JAVA 和C两种语言的客户端。
3、znode节点
zookeeper中数据基本单元叫节点,节点之下可包含子节点,最后以树级方式程现。每个节点拥有唯一的路径path。客户端基于PATH上传节点数据,zookeeper 收到后会实时通知对该路径进行监听的客户端。
二、环境搭建和常规配置
zookeeper基于JAVA开发,下载后只要有对应JVM环境即可运行。其默认的端口号是2181运行前得保证其不冲突。
1、环境下载
只需要按照jdk1.8以上的环境就可以了,然后用如下命令下载zookeeper包,当然也可以直接到https://zookeeper.apache.org到官网去找自己合适的版本。
wget https://mirrors.tuna.tsinghua.edu.cn/apache/zookeeper/zookeeper-3.6.2/apache-zookeeper-3.6.2-bin.tar.gz
- apache-zookeeper-xxx-tar.gz 代表源代码
- apache-zookeeper-xxx-bin.tar.gz 运行版本
2、解压与启动
解压
tar -zxvf apache-zookeeper-3.6.2-bin.tar.gz
mv apache-zookeeper-3.6.2-bin zk
用默认配置
到zk里面的config目录下执行
cp zoo_sample.cfg zoo.cfg
启动
进入zk的bin目录下执行
./zkServer.sh start
注:win下面用zkServer.cmd
3、常规配置文件说明
# zookeeper时间配置中的基本单位 (毫秒)
tickTime=2000
# 允许follower初始化连接到leader最大时长,它表示tickTime时间倍数 即:initLimit*tickTime
initLimit=10
# 允许follower与leader数据同步最大时长,它表示tickTime时间倍数
syncLimit=5
#zookeper 数据存储目录
dataDir=/tmp/zookeeper
#对客户端提供的端口号
clientPort=2181
#单个客户端与zookeeper最大并发连接数
maxClientCnxns=60
# 保存的数据快照数量,之外的将会被清除
autopurge.snapRetainCount=3
#自动触发清除任务时间间隔,小时为单位。默认为0,表示不自动清除。
autopurge.purgeInterval=1
三、客户端基本命令
在bin目录下执行./zkCli.sh进入客户端
1、基本命令列表
#添加用户,语法在后面权限内会详细去说。
addauth scheme auth
#断开当前客户端和服务端的连接
close
# 动态加载配置
config [-c] [-w] [-s]
# 连接到客户端
connect host:port
#创建节点 -e 临时节点(不允许有子节点) -s 序列节点 -c 默认节点 (path为路径) 【data为数据】 【acl权限】 -t节点存活时间。
create [-s] [-e] [-c] [-t ttl] path [data] [acl]
#删除节点(不能带有子节点) [-v version] 版本号,一般不用
delete [-v version] path
# 删除节点(包含其子节点)
deleteall path
# 删除节点限额 -n 子节点数 -b 字节数
delquota [-n|-b] path
# 取得节点的值 -s 取值和状态,-w 添加监听(监听数据)
get [-s] [-w] path
#取得限权,-s取权限和状态
getAcl [-s] path
# 历史操作记录
history
# 查看节点限额
listquota path
#查看节点 -s查看节点和内容 -w(添加监听是否添加或删除子节点,但不会监听子节点值的变化) -R查到所有节点(包含根节点)
ls [-s] [-w] [-R] path
#相当于ls和stat的组合
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[,...]*]
# 重新操作某命令,与history配合使用
redo cmdno
#移除监听
removewatches path [-c|-d|-a] [-l]
# 删除和deleteAll完全一致(已被deleteAll替换,不建议继续使用)
rmr path
# 给节点赋值 -s返回节点状态
set [-s] [-v version] path data
#设置节点权限(后面会详细说一下这个)
setAcl [-s] [-v version] [-R] path acl
#设置节点限额 -n 子节点数 -b 字节数
setquota -n|-b val path
# 查看节点状态,-w同get -w用法
stat [-w]path
# 强制同步
sync path
2、节点的增删改查
# 列出子节点
ls /
#创建节点
create /test "test"
# 查看节点
get /test
# 创建子节点
create /test/a "a"
# 删除节点
delete /test/a
# 删除所有节点 包括子节点
deleteall /test
四、Zookeeper节点介绍
zookeeper中节点叫znode存储结构上跟文件系统类似,以树级结构进行存储。不同之外在于znode没有目录的概念,不能执行类似cd之类的命令。znode结构包含如下:
- path:唯一路径
- childNode:子节点
- stat:状态属性
- type:节点类型
- data:节点数据
1、节点类型
类型 | 描述 |
---|---|
PERSISTENT | 持久节点 |
PERSISTENT_SEQUENTIAL | 持久序号节点 |
EPHEMERAL | 临时节点(不可在拥有子节点) |
EPHEMERAL_SEQUENTIAL | 临时序号节点(不可在拥有子节点) |
1-1、PERSISTENT(持久节点)
持久化保存的节点,也是默认创建的
#默认创建的就是持久节点
create /test
1-2、PERSISTENT_SEQUENTIAL(持久序号节点)
创建时zookeeper会在路径上加上序号作为后缀,。非常适合用于分布式锁、分布式选举等场景。创建时添加 -s 参数即可。
#创建序号节点
create -s /test
#返回创建的实际路径
created /test0000000001
create -s /test
#返回创建的实际路径2
created /test0000000002
1-3、EPHEMERAL(临时节点)
临时节点会在客户端会话断开后自动删除。适用于心跳,服务发现等场景。创建时添加参数-e 即可。
#创建临时节点, 断开会话 在连接将会自动删除
create -e /temp
1-4、EPHEMERAL_SEQUENTIAL(临时序号节点)
与持久序号节点类似,不同之处在于EPHEMERAL_SEQUENTIAL是临时的会在会话断开后删除。创建时添加 -e -s
create -e -s /temp/seq
2、节点属性
# 查看节点属性
stat /test
其属性说明如下表:
#创建节点的事务ID
cZxid = 0x385
#创建时间
ctime = Tue Sep 24 17:26:28 CST 2020
#修改节点的事务ID
mZxid = 0x385
#最后修改时间
mtime = Tue Sep 24 17:26:28 CST 2020
#子节点变更的事务ID
pZxid = 0x385
#这表示对此znode的子节点进行的更改次数(不包括子节点)
cversion = 0
# 数据版本,变更次数
dataVersion = 0
#权限版本,变更次数
aclVersion = 0
#临时节点所属会话ID 为0则表示为持久节点
ephemeralOwner = 0x0
#数据长度
dataLength = 17
#子节点数(不包括子子节点)
numChildren = 0
3、节点的监听
添加-w参数可实时监听节点与子节点的变化,并且实时收到通知。非常适用保障分布式情况下的数据一至性。其使用方式如下:
命令 | 描述 |
---|---|
ls -w path | 监听子节点的变化(增,删) |
get -w path | 监听节点数据的变化 |
stat -w path | 监听节点属性的变化 |
printwatches on\off | 触发监听后,是否打印监听事件(默认on) |
注1:节点的监听是一次性的,客户端得到监听变化后需要重新发起监听。
注2:临时节点在客户端主动close后会立马被清除,但是若是客户端被动kill掉,则需要超时时间过后才会在别的客户端监控到被清除
五、ACL权限设置
ACL全称为Access Control List(访问控制列表),用于控制资源的访问权限。ZooKeeper使用ACL来控制对其znode的防问。基于schemepermission的方式进行权限控制。scheme表示授权模式、id模式对应值、permission即具体的增删改权限位。
方案 | 描述 |
---|---|
world | 开放模式,world表示全世界都可以访问(这是默认设置) |
ip | ip模式,限定客户端IP防问 |
auth | 用户密码认证模式,只有在会话中添加了认证才可以防问 |
digest | 与auth类似,区别在于auth用明文密码,而digest 用sha-1+base64加密后的密码。在实际使用中digest 更常见。 |
permission权限位
权限位 | 权限 | 描述 |
---|---|---|
c | CREATE | 可以创建子节点 |
d | DELETE | 可以删除子节点(仅下一级节点) |
r | READ | 可以读取节点数据及显示子节点列表 |
w | WRITE | 可以设置节点数据 |
a | ADMIN | 可以设置节点访问控制列表权限 |
acl 相关命令:
命令 | 使用方式 | 描述 |
---|---|---|
getAcl | getAcl |
读取ACL权限 |
setAcl | setAcl |
设置ACL权限 |
addauth | addauth |
添加认证用户 |
1、ACL权限特点
- 1、zookeeper的权限是基于znode节点的,需要对每个节点设置权限。
- 2、znode节点支持同时设置多种权限方案和多个权限。当znode有多种权限的时候,只要有一个权限允许当前操作,即可执行当前操作,即多个权限之间为或的关系。
- 3、子节点不会继承父节点的权限,客户端没有权限访问当前节点,但是可以访问当前节点的子节点。
- 4、使用setAcl命令对节点进行权限设置会覆盖掉原来的权限。
2、权限相关命令
addauth scheme auth // 添加认证用户
getAcl path // 获取指定节点的权限列表
setAcl [-v version] [-R] path acl // 对指定节点设置权限。
3、添加认证用户:addauth
添加认证用户命令:
# 通常情况下,schame固定为digest,auth为user:passwd。(目前只接触这种用法)
addauth scheme auth
添加认证用户实例:
# 添加第一个认证用户
addauth digest abc:123456
# 添加第二个认证用户,注意:第一个添加的认证用户依然有效。
addauth digest ab:123
# 添加第三个认证用户,注意,单个用户可以有不同的密码,此时三个认证的用户全部同时有效
addauth digest abc:abcdefg
注意问题:
1、认证用户添加完之后,认证将对该会话中添加认证之后的所有操作都有效,一旦会话结束,认证失效。
2、添加认证时不同认证用户可以有不同的密码,多个user:passwd都有效。
4、设置znode节点操作权限命令:setAcl
设置znode节点操作权限命令:
setAcl [-v version] [-R] path acl // 对指定节点设置权限。
设置znode节点选项说明:
-v : 指定权限版本,即aclVersion,若指定版本与当前节点的ACL版本不一致,修改znode节点权限将失败。
-R : 递归设置权限,设置当前节点下的递归子节点的权限为acl,特别注意:权限只有当前拥有的权限有效,而新创建的子节点不会继承该权限。
六、ACL权限设置举例
1、world ACL授权策略
world授权策略从字面意义上讲就是世界,即为对于所有人进行赋予权限。在world授权模式下,id的取值只有一种,即为anyone,所以该模式下,其设置权限的命令就固定为:
setAcl /test world:anyone:${permission}
使用实例,该例子中,对节点/test_aworld进行赋予所有人都可以对该节点进行读读取和权限操作:
[zk: localhost:2181(CONNECTED) 11] create /test_aworld
Created /test_aworld
[zk: localhost:2181(CONNECTED) 12] setAcl /test_world world:anyone:ra
Node does not exist: /test_world
[zk: localhost:2181(CONNECTED) 13] setAcl /test_aworld world:anyone:ra
[zk: localhost:2181(CONNECTED) 15] getAcl /test_aworld
'world,'anyone
: ra
注意:创建节点之后,节点的默认权限为所有人都可以对节点进行所有操作
即为world:anyone:cdrwa权限,可创建节点之后进行权限查看:
[zk: localhost:2181(CONNECTED) 16] create /test
Created /test
[zk: localhost:2181(CONNECTED) 17] getAcl /test
'world,'anyone
: cdrwa
2、ip ACL授权策略
ip授权策略即为限制指定ip可以对当前节点进行指定操作。在ip授权策略下,id的取值为被赋予权限的IP,我们也可以对节点进行多个ip赋予权限,不同ip权限使用逗号隔开。
[zk: localhost:2181(CONNECTED) 18] create /test_ip
Created /test_ip
# 赋予单个ip节点权限
[zk: localhost:2181(CONNECTED) 19] setAcl /test_ip ip:127.0.0.1:cdrwa
[zk: localhost:2181(CONNECTED) 20] getAcl /test_ip
'ip,'127.0.0.1
: cdrwa
# 赋予多个ip节点权限,权限之间使用逗号隔开
[zk: localhost:2181(CONNECTED) 21] setAcl /test_ip ip:127.0.0.1:cdrwa,ip:127.0.0.2:cdr
[zk: localhost:2181(CONNECTED) 22] getAcl /test_ip
'ip,'127.0.0.1
: cdrwa
'ip,'127.0.0.2
: cdr
3、auth ACL授权策略
auth授权策略为给(1)当前会话中 (2)设置权限之前 (3)所有授权过的所有用户赋予权限。在当前模式下,权限设置中的id字段为空,如果填写也会被zk忽略。从上面对auth授权策略的解释可以看出,设置auth ACL权限必须要有个前提,即为在设置权限之前需要添加授权认证用户,若没有添加,则设置权限会报错。
[zk: localhost:2181(CONNECTED) 1] create /test_auth auth
Created /test_auth
# 在设置权限之前没有添加认证用户,直接设置权限报错。
[zk: localhost:2181(CONNECTED) 2] setAcl /test_auth auth::cdrwa
Acl is not valid : /test_auth
# 首先添加三个认证用户,注意认证用户可以有不同的密码,相当于有两种身份
[zk: localhost:2181(CONNECTED) 3] addauth digest user1:123
[zk: localhost:2181(CONNECTED) 4] addauth digest user2:123
[zk: localhost:2181(CONNECTED) 5] addauth digest user1:12
[zk: localhost:2181(CONNECTED) 6] setAcl /test_auth auth::cdrwa
[zk: localhost:2181(CONNECTED) 7] getAcl /test_auth
'digest,'user1:Nv3cjIteQ1W3F6fZb+mYaNSs5rc=
: cdrwa
'digest,'user2:gZ5Cvtv1mYP7pvsOM/1dcB8DgR0=
: cdrwa
'digest,'user1:5JrZvgh2b8BZ0ZjBlnn0pK9h0Ww=
: cdrwa
# 断开当前链接
[zk: localhost:2181(CONNECTED) 8] close
WATCHER::
WatchedEvent state:Closed type:None path:null
[zk: localhost:2181(CLOSED) 9] connect 127.0.0.1:2181
[zk: 127.0.0.1:2181(CONNECTING) 10]
WATCHER::
WatchedEvent state:SyncConnected type:None path:null
# 访问当前节点,会报错没有权限
[zk: 127.0.0.1:2181(CONNECTED) 10] get /test_auth
org.apache.zookeeper.KeeperException$NoAuthException: KeeperErrorCode = NoAuth for /test_auth
# 添加认证用户之后进行访问,即可有权限操作当前节点。
[zk: 127.0.0.1:2181(CONNECTED) 11] addauth digest user1:123
[zk: 127.0.0.1:2181(CONNECTED) 12] get /test_auth
auth
4、digest ACL授权策略
digest授权策略即为用户名密码的授权策略,也是生产中最常用的一种方式。与auth授权策略不同的是,digest授权策略在设置权限之前不需要添加认证用户,digest中的id为指定可以访问的用户名:密码健值对,其中密码为对(user:明文密码)进行SHA1之后再进行BASE64的到的编码。 同样的,我们也可以对同一个节点设置不同的用户名密码,对不同的用户名密码设置不同的权限,这个有很大的作用,我们可以创建一个管理员用户以及一个guest用户,使用同一个znode节点对外提供服务。
要设置digest权限控制,首先需要通过用户和明文密码进行生成密文。生成密文有很多中方式,最简单的就是使用Linux提供的工具进行生成,计算密文方式如下:
echo -n <user>:<password> | openssl dgst -binary -sha1 | openssl base64
使用实例:
1、首先我们选取w1:test1234作为用户名密码,然后生成密码对应的密文:
[root@VM_centos ~]# echo -n w1:test1234 | openssl dgst -binary -sha1 | openssl base64
gqaq4OYVOJwJ2x+ppzJfq8Jy07Y=
2、然后我们创建znode节点并对节点进行设置权限。
[zk: localhost:2181(CONNECTED) 1] create /test_digest
Created /test_digest
# 首先对节点设置digest ACL权限
[zk: localhost:2181(CONNECTED) 2] setAcl /test_digest digest:w1:gqaq4OYVOJwJ2x+ppzJfq8Jy07Y=:cdrwa
# 查询当前节点权限,由于当前没有添加回话信息,所以返回认证失败
[zk: localhost:2181(CONNECTED) 3] getAcl /test_digest
Authentication is not valid : /test_digest
# 添加用户认证,再进行权限访问,访问成功。
[zk: localhost:2181(CONNECTED) 4] addauth digest w1:test1234
[zk: localhost:2181(CONNECTED) 5] getAcl /test_digest
'digest,'w1:gqaq4OYVOJwJ2x+ppzJfq8Jy07Y=
: cdrwa
# 同样的 我们可以进行设置多个digest权限,针对不同的用户有不同的权限
[zk: localhost:2181(CONNECTED) 7] setAcl /test_digest digest:w1:gqaq4OYVOJwJ2x+ppzJfq8Jy07Y=:cdrwa,digest:w2:gQXogR4k0JPSqlvthbuiYOnAhAM=:cdra
[zk: localhost:2181(CONNECTED) 8] getAcl /test_digest
'digest,'w1:gqaq4OYVOJwJ2x+ppzJfq8Jy07Y=
: cdrwa
'digest,'w2:gQXogR4k0JPSqlvthbuiYOnAhAM=
: cdra
5、auth与digest权限控制区别
上面四种授权策略中,ip和world授权方式比较容易理解,但是auth与digest比较容易混淆,网上很多文章也说的不太清楚,所以这里总结一下这两种授权方式的区别。
- auth授权之前必须在当前会话中添加授权用户,若不添加,设置权限报错;而digest授权方式无需在授权之前添加认证用户
- auth授权中id为空,如果填写也会被忽略;digest授权id为(用户名:密文)的格式。
- auth只能针对多个授权用户进行相同权限的赋予;而digest可以自定义不同用户对该节点有不同的权限,在该节点的管理员、普通用户权限设置比较友好。