zk分享(1)-zk使用
概述
分布式协调技术
分布式协调技术 主要用来解决分布式环境当中多个进程之间的同步控制,让他们有序的去访问某种临界资源,防止造成”脏数据”的后果。
那么怎么对这些进程进行调度呢?
这时候我们就需要一个协调器,来让他们有序的来访问这个资源。这个协调器就是我们经常提到的那个锁。通过这个锁机制,我们就能保证了分布式系统中多个进程能够有序的访问该临界资源。那么我们把这个分布式环境下的这个锁叫作分布式锁。但是因为其运行所在的环境存在网络延迟等不可靠因素的,导致对数据的处理存在许多困难。目前处理分布式协调技术比较好的有Chubby(Google产品,未开源)和Zookeeper(Apache产品,开源)。
Zookeeper
ZooKeeper is a centralized service for maintaining configuration information, naming, providing distributed synchronization, and providing group services. All of these kinds of services are used in some form or another by distributed applications. Each time they are implemented there is a lot of work that goes into fixing the bugs and race conditions that are inevitable. Because of the difficulty of implementing these kinds of services, applications initially usually skimp on them, which make them brittle in the presence of change and difficult to manage. Even when done correctly, different implementations of these services lead to management complexity when the applications are deployed.
ZooKeeper是一种集中式服务,用于维护配置信息,命名,提供分布式同步和提供组服务。 所有这些类型的服务都以分布式应用程序的某种形式使用。 每次实施它们都需要做很多工作来修复不可避免的错误和竞争条件。 由于难以实现这些类型的服务,应用程序最初通常会忽视它们,这使得它们在变化的情况下变得脆弱并且难以管理。 即使正确完成,这些服务的不同实现也会在部署应用程序时导致管理复杂性。
Apache ZooKeeper是一个用于分布式协调的系统。它支持实现对分布式设置中的安全性和活跃性至关重要的各种原语和机制,例如分布式锁定,主选举,组成员身份和配置管理。
zookeeper应用
数据结构
在zookeeper下,其文件存储类似于树一样具有层次结构,每个文件节点被称之为Znode
- 每个Znode存储数据大小具有限制(默认节点最大数据量为1M,通过环境变量jute.maxbuffer设置)
- Znode通过路径引用,但是其路径必须是绝对的,因此他们必须由斜杠字符来开头

节点类型
- 持久节点(persistent)
持久的znode,如/path,只能通过调用delete来进行删除。 - 临时节点(ephemeral)
当创建该节点的客户端崩溃或关闭了与ZooKeeper的连接时,这个节点就会被删除。 - 有序节点(sequential)
一个znode还可以设置为有序(sequential)节点。一个有序znode节点被分配唯一个单调递增的整数。当创建有序节点时,一个序号会被追加到路径之后。例如,如果一个客户端创建了一个有序znode节点,其路径为/tasks/task-,那么ZooKeeper将会分配一个序号,如1,并将这个数字追加到路径之后,最后该znode节点为/tasks/task-1。有序znode通过提供了创建具有唯一名称的znode的简单方式。
znode一共有4种类型:持久的(persistent)、临时的(ephemeral)、持久有序的(persistent_sequential)和临时有序的(ephemeral_sequential)。
监视器
客户端向ZooKeeper注册需要接收通知的znode,通过对znode设置监视点(watch)来接收通知。监视点是一个单次触发的操作,意即监视点会触发一个通知。为了接收多个通知,客户端必须在每次通知后设置一个新的监视点。
API操作
命令行
命令行连接服务器:./zkCli.sh -server ip:port
查询
列出节点
ls path [watch]
1
2
3
4
5
6[zk: 127.0.0.1:2181(CONNECTED) 1] ls /
[zookeeper]
[zk: 127.0.0.1:2181(CONNECTED) 2] ls /zookeeper
[quota]
[zk: 127.0.0.1:2181(CONNECTED) 3] ls /zookeeper/quota
[]查询节点状态
stat path [watch]
1
2
3
4
5
6
7
8
9
10
11
12[zk: localhost:2181(CONNECTED) 8] stat /
cZxid = 0x0 #节点被创建时的事务ID
ctime = Thu Jan 01 08:00:00 CST 1970 #节点被创建的时间
mZxid = 0x0 #最近一次更新的时的事务ID
mtime = Thu Jan 01 08:00:00 CST 1970 #最近一次更新的时间
pZxid = 0x1a5 #该节点的子节点列表最近一次被修改的事务ID
cversion = 99 #子节点的版本号
dataVersion = 0 #数据版本
aclVersion = 0 #ACL版本号
ephemeralOwner = 0x0 #创建临时节点的事务ID,如果是持久节点,则该节点为0x0
dataLength = 0 #当前节点的数据长度
numChildren = 3 #当前节点的子节点数目查询节点中的数据
get path [watch]
1
2[zk: localhost:2181(CONNECTED) 10] get /test
demo列出子节点并查询当前节点状态
ls2 path [watch]
1
查看命令历史
history
1
2
3
4
5
6
7[zk: localhost:2181(CONNECTED) 5] history
0 - ls -l
1 - ls /
2 - delquota -n /testquota
3 - ls /
4 - listquota /testquota
5 - history执行之前的命令
redo 命令行数(history查询获得)
1
2[zk: localhost:2181(CONNECTED) 6] redo 1 #redo重新执行先前命令,根据行数执行
[testquota, zookeeper, hellozk]
创建
create [-s] [-e] path data acl
其中:括号中时可选的,s表示创建永久节点,e表示创建临时节点,acl表示访问控制列表1
2
3
4[zk: localhost:2181(CONNECTED) 2] create /test demo
Created /test
[zk: localhost:2181(CONNECTED) 5] create /test/test1 demo1
Created /test/test1
修改
set path data [dateVersion]1
2
3
4
5
6
7
8
9
10
11
12[zk: localhost:2181(CONNECTED) 13] set /test demo2
cZxid = 0x1a5
ctime = Thu Jun 07 08:26:10 CST 2018
mZxid = 0x1a7
mtime = Thu Jun 07 08:45:54 CST 2018 #修改时间
pZxid = 0x1a6
cversion = 1
dataVersion = 1 #数据版本号为1,表示被更改一次
aclVersion = 0
ephemeralOwner = 0x0
dataLength = 5
numChildren = 1
删除
删除节点
delete path [dateVersion]
只能删除不含子节点的节点
1
[zk: localhost:2181(CONNECTED) 25] delete /test
递归删除
rmr path
能递归删除节点
节点限制
增加限制
setquota -n | -b val path
删除限制
delquota -n | -b val path
Java
zookeeper client包有两种,一种是官方提供的,另一种是Netflix提供的1
2
3
4
5
6
7
8
9
10<dependency>
<groupId>org.apache.zookeeper</groupId>
<artifactId>zookeeper</artifactId>
<version>3.4.13</version>
</dependency>
<dependency>
<groupId>org.apache.curator</groupId>
<artifactId>curator-recipes</artifactId>
<version>4.1.0</version>
</dependency>
获取所有子节点
以下为不设置监视器的情况1
zooKeeper.getChildren(path, false);
查看节点数据
直接返回节点中保存的数据,stat中会记录状态信息1
2Stat stat = new Stat(); // 保存节点的状态信息
byte[] date = zooKeeper.getData(path, null, stat);
创建节点
1 | List<ACL> acls = new ArrayList<>(); |
ACL
身份的认证有4种方式:
- world:默认方式,相当于全世界都能访问
- auth:代表已经认证通过的用户(cli中可以通过addauth digest user:pwd 来添加当前上下文中的授权用户)
- digest:即用户名:密码这种方式认证,这也是业务系统中最常用的
- ip:使用Ip地址认证
1 | ACL acl = new ACL(ZooDefs.Perms.ALL, |
验证身份1
zooKeeper.addAuthInfo(digest, authAccount.getBytes());
设置节点数据
1 | zooKeeper.setData(path, data.getBytes(), stat.getVersion()); |
删除节点
1 | zooKeeper.setData(path, data.getBytes(), stat.getVersion()); |
Multiop
Multiop并非ZooKeeper的原始设计,该特性在3.4.0版本中被添加进来。Multiop可以原子性地执行多个ZooKeeper的操作,执行过程为原子性,即在multiop代码块中的所有操作要不全部成功,要不全部失败。例如,我们在一个multiop块中删除一个父节点以及其子节点,执行结果只可能是这两个操作都成功或都失败,而不可能是父节点被删除而子节点还存在,或子节点被删除而父节点还存在。
java:1
2
3
4
5
6
7
8List<Op> ops = new ArrayList<>();
ops.add(Op.create("/mytest", null, null, CreateMode.EPHEMERAL));
ops.add(Op.create("/mytest/addr-", null, null, CreateMode.EPHEMERAL_SEQUENTIAL));
ops.add(Op.setData("/path", "data".getBytes(), -1));
ops.add(Op.check("/path", -1));
ops.add(Op.delete("/path", -1));
List<OpResult> result = zooKeeper.multi(ops);
zk分布式锁
- 建立一个锁目录/xxlock
- 获取锁:
- 向锁目录下创建临时序列目录(EPHEMERAL_SEQUENTIAL);
- 获取锁目录下的所有子节点;
- 按照序列从小到大排列;
- 若当前创建的目录在多列中是排列在第一位的,则获取了锁;
- 若不是则使用getData在前一个序列上设置Watch;
- 当释放锁时删除目录,会通过Watch通知下一个节点去获取锁;
- 释放锁:
- 删除对应的目录