zookeeper:znode

overview

zookeeper内部存储数据的数据结构是一棵树,类似于linux文件系统的组织方式。
最上层有一个根节点 /,根节点下有子节点,字节点下又能递归地拥有子节点。
没有任何子节点的节点称之为叶子节点。
在zookeeper的术语中,节点称之为znode
zookeeper:znode_第1张图片

zookeeper只是在内存中维护这样一份树状的数据结构,并没有直接提供分布式系统协调中的一些机制。
分布式锁、集群配置管理、以及master选举等,都需要应用在zookeeper这个基本的数据结构上去做实现。
例如,当多个客户端要竞争同一把锁的时候,每个客户端都尝试去创建某个znode。
zookeeper保证了一个znode只能由一个客户端创建,哪个客户端创建成功了,它便拥有了这个锁。

znode

存储数据类型

每个znode除了有代表自己的路径名外,还能存储一些数据,zookeeper只支持以字节数组的方式去存储数据。
如果我们要在znode里存储一个字符串,很简单,直接调字符串的getBytes方法就可以。
如果我们要在znode里存储一个Java对象呢,那么就得先把这个对象在本地序列化后再存储到zookeeper。

持久性和挥发性

zookeeper中,一个znode会有两种可能的mode:

  • 持久性的(persistent)
  • 挥发性的(ephemeral)

这个mode在创建znode的时候就确定下来,默认情况下,创建的znode是持久性的。
持久性和挥发性各有各的应用场景,持久性的znode会一直存在,因此持久性的znode适合用来存储一些全局性,共享性的数据。
比如说,配置信息,任务信息。
挥发性的znode在客户端的session结束后或者是zookeeper服务器停止运行的时候会被删除。
挥发性的znode适合用来存储与会话相关的数据,比如说一个客户端的状态,一个客户端持有的锁,与会话相关的数据,应随着会话的生死而生死。

一个比较经典的案例是,在分布式锁的场景下,如上文所提到的,各个想要获取这把锁的客户端都会去创建同一个znode。
哪个客户端创建成功,便获取到了这把锁。
在这里,这个znode必须是挥发性的,如果一个客户端获取锁以后,它自己宕机了,要是这个znode不是挥发性的,那么它将永远存在于zookeeper中。
这也意味着其它客户端永远也不可能获得这个锁。

值得注意的是,如果一个znode是挥发性的,目前zookeeper是不允许它有子znode的。
zookeeper:znode_第2张图片

-e选项指定创建一个挥发性的znode,然后在这个znode下创建子znode时,提示挥发性的节点不允许有子节点
zookeeper未来可能会考虑支持创建子节点。

自增长的znode

当我们需要在一个znode下创建子节点,并希望这些子节点都拥有唯一的名字时,我们可以从两方面去做到:

  • 客户端主动设置名字,名字的唯一性由客户端来保证,这也意味着客户端必须有生成全局唯一ID的能力或机制。
  • 交给zookeeper去做,由zookeeper来生成唯一的ID

第二种情形很类似于数据库的auto_increment,当我们在数据库创建了一个表,并把某个字段,比如说ID字段,设置为自增长的。
那么每次我们往表中插入一条数据,不必为ID指定值,数据库自动会以自增长序列的方式作为ID的值。

zookeeper也有这个机制,比如我们创建了一个代表任务的节点/tasks,这个znode下会有task-1,task-2,......,task-n等,如下所示
zookeeper:znode_第3张图片

结合持久性、挥发性以及是否自增长,一个znode会有四种模式:持久的、挥发的、持久并自增长的、挥发并自增长的。

监听

zookeeper允许客户端对某个znode进行监听。
比如说这个znode的内容改变了;
或者这个znode原本不存在,但被创建了;
或者这个znode被删除了;
又或者是有人在这个znode下创建子节点了;
等等。

客户端只需要向zookeeper注册对某个znode的监听,之后zookeeper会将该znode上发生的变化告诉客户端。
值得注意的是这里,zookeeper通知客户端的时候,只会告诉客户端发生了什么事,但并不会把发生改变的数据推送给客户端。
客户端需要在接收到通知后,自己主动去查询zookeeper。
另外,客户端对某个节点的监听并不是永久有效的,监听只能用一次,如果下次还想再监听,客户端就必须不断地注册监听。

例如以下例子,启动一个客户端client1在/下创建了一个/test的节点
zookeeper:znode_第4张图片

另外启动一个客户端client2,对/test的数据变化进行监听

zookeeper:znode_第5张图片

然后用client1对/test存储的数据进行修改

zookeeper:znode_第6张图片

可以发现client2收到了一些推送消息,类型是NodeDataChanged,意味着znode的数据被更改了

此时如果在client1上再次对/test的内容进行更改,client2是收不到通知的。
如前文所说,每一次监听只能用一次,如果client2想要继续收到通知,那么只能继续注册监听。

znode的版本

每个znode都有一个版本号,每次对znode的修改都会使版本号递增。
为znode进行set data或者delete操作时,可以指定版本号,只有指定的版本号是znode当前的版本号时,操作才能成功。
这个版本号的作用类似于我们常常见到的CAS(compare and swap)操作,目的是避免加锁。
适合用于多个客户端并发去修改同一个znode的情况。

扫一扫关注我的微信公众号

你可能感兴趣的:(zookeeper:znode)