前一篇我们讲了Zookeeper服务通过zkServer.sh就可以启动,同样的,使用 zkCli.sh 就可以启动Zookeeper的客户端,默认连接本地的Zookeeper服务。

zookeeper教程文档 zookeeper如何使用_数据

通过 help

zookeeper教程文档 zookeeper如何使用_数据_02

1、create - 创建节点

在 / 目录下创建一个节点:create /mingjiao

zookeeper教程文档 zookeeper如何使用_zookeeper_03

新版本中只创建节点,后面不跟节点的数据也能够创建成功,但是我记得以前老版本是创建节点的时候后面必须跟上数据,否则的虽然不会报错,但节点是不会创建成功的。

节点之中依然可以嵌套创建节点:create /mingjiao/hujiaofawang

zookeeper教程文档 zookeeper如何使用_客户端_04

2、set - 写入数据 / get - 读取数据 

在 /mingjiao/hujiaofawang 节点中写入数据:

set /mingjiao/hujiaofawang "xiexun"

再通过 get /mingjiao/hujiaofawang 读取 /mingjiao/hujiaofawang 节点中的数据

get /mingjiao/hujiaofawang

zookeeper教程文档 zookeeper如何使用_Redis_05

每个节点中最多只能放1M的数据,并且这个数据的存储和Redis一样也是二进制安全(关于二进制安全,我之前专门写过一篇相关的文章做过详细说明,感兴趣的可以看一看:


get 后面可以加上 -s 参数查看数据相关的详细信息:

get -s /mingjiao/hujiaofawang

zookeeper教程文档 zookeeper如何使用_zookeeper教程文档_06

cZxid 就是上一篇中提到的事务id。Zookeeper的顺序执行就体现在这个cZxid上:当客户执行增、删、改操作的时候,无论连接的是哪一个服务,最终都会递交到leader去执行。leader本身是一个单机节点,单机维护一个单调递增的计数器很容易。

其中"c"代表的就是create的意思,就是说刚刚写入 "xiexun" 这个数据的时候,维护的相关事务号就是3。

cZxid一共由4个字节组成,看到0x开头就知道这个十六进制,在十六进制中每一个位代表四个二进制位,所以 0x200000003 中,后面八个十六进制位(三十二个二进制位)"00000003" 用于表示事务id;前面的这个 2 表示的是leader的纪元(说人话就是第几个leader),这正印证了上一篇中Zookeeper搭建的时候,原先第1个leader是server.3,后来宕机以后现在新的leader是server.4,而它正是第2个leader。

mZxid 则是修改的事务id,pZxid 表示的是当前的这个节点下,创建的最后的一个节点的事务id,这个要怎么理解?

现在来看 /mingjiao 这个节点的pZxid:get -s /mingjiao

zookeeper教程文档 zookeeper如何使用_数据_07

经过一系列操作之后,最终创建最后一个节点:create /mingjiao/wusanren "zhoudian"

zookeeper教程文档 zookeeper如何使用_数据_08

此时再看 /mingjiao 这个节点的pZxid:get -s /mingjiao

zookeeper教程文档 zookeeper如何使用_zookeeper_09

对于 /mingjiao 来讲,这个节点下创建的最后一个节点是 /mingjiao/wusanren,所以 /mingjiao 的pZxid 就是 /mingjiao/wusanren 的 cZxid。

ephemeralOwner = 0x0

create -e /dali

set /dali "duanzhixing"

get -s /dali

zookeeper教程文档 zookeeper如何使用_数据_10

现在 ephemeralOwner 就有了值,代表当前这个节点是一个临时节点,而且这个值看着是不是有点眼熟?没错,在客户端连接上来的时候会有一个sessionId,而这个临时节点的值就是用这个sessionId作为基础而生成,这也印证了我上一篇所说的每一个客户端连接到任意Zookeeper的时候,都会建立一个session,而Zookeeper中分布式锁的使用与这个session密不可分。

zookeeper教程文档 zookeeper如何使用_Redis_11

临时节点什么时候消失呢?只要客户端连接一断开,基于session所创建的临时节点就会消失。

quit

zkCli.sh

get -s /dali

zookeeper教程文档 zookeeper如何使用_客户端_12

3、Zookeeper集群统一视图证明

关于这个sessionId有一点需要强调的是:当通过Java API去连接Zookeeper的时候,假设第1次连接的是server.1,客户端将server.1的sessionId缓存在本地。断开与server.1的连接后,再次连接server.2,那么:server.2中有没有客户端缓存server.1的sessionId?

上一篇中我们在Zookeeper的官网中有看到Zookeeper有这么一个特性:统一视图。统一视图统一的不仅仅是数据,还包括session。当客户端连接到一个Zookeeper的时候,产生session之后,当前这个Zookeeper会将session同步到集群中其它的Zookeeper。

① 执行 zk.Cli 连接server.4

zookeeper教程文档 zookeeper如何使用_zookeeper教程文档_13

虽然我们看不到这个session,但是我们可以通过事务id消耗来侧面印证这件事: Zookeeper只要动用统一视图同步数据的话,就一定要消耗事务id。因为Zookeeper要把创建的这个session同步给所有其它的zookeeper,就必须要走事务来保证一致性。

② 在server.4上执行:

create /baituoshanzhuang "ouyangfeng"

get -s /baituoshanzhuang

zookeeper教程文档 zookeeper如何使用_客户端_14

③ 执行 zk.Cli 连接server.2

zookeeper教程文档 zookeeper如何使用_数据_15

只是建立连接,不做任何操作。 

④ 在server.4上执行:

create /gumupai "linchaoying"

get -s /gumupai

zookeeper教程文档 zookeeper如何使用_zookeeper_16

上一次创建节点后 cZxid = 0x200000014,这一次创建节点后 cZxid = 0x200000016,刚好两次创建节点之间 cZxid 相差了1次,而这一次的事务就是用来将sessionId同步到其它的Zookeeper上。也就是说如果通过Java API将sessionId缓存到本地,和当前连接的Zookeeper断开之后,只要在配置好的规定的时间内重新连接到其他的Zookeeper,之前创建的节点并不会消息。

4、Zookeeper中的序列节点

上一篇中我们也说过Zookeeper中的节点不论是永久节点还是临时节点,它们都是序列节点。要怎么理解这个序列节点?

当有很多个客户端,它们都想在某一个节点下创建自己的节点存储数据,在分布式情况下大家对着一个节点疯狂的写入,极有可能产生覆盖的情况。

规避这个问题也很简单,在使用 create 命令的时候,后面有参数可携带:create [-s] [-e] [-c] [-t ttl] path [data] [acl],[-s]可以避免覆盖的问题。

zookeeper教程文档 zookeeper如何使用_客户端_17

这个节点后面的递增其实和事务id后面的递增一样,都是由leader单机维护,不仅能规避覆盖写入的问题,还能满足分布式情况下的统一命名。

5、Zookeeper的实际使用

前面介绍了一大堆Zookeeper的这个特点那个特点,那么这个Zookeeper在企业中实际用途到底是什么?根据zookeeper官方的描述,可以使用的场景其实很多:

① 统一配置管理:统一视图 + 可存储1M数据

② 分组管理:父子结构,Path路劲

③ 统一命名:序列节点

④ 同步:临时节点

根据以上特性能想到的最典型的应用场景就是:分布式锁。如果这把锁依托一个父节点,且具备-s,则意味着这个父节点下可以有多把锁。在这多把锁中后面的锁紧紧且仅仅盯住前面的那把锁,就等于变相的实现了一种队列式的带事务的锁模型。

第二个典型的应用场景则是:HA选主。