ZooKeeper

节点


节点类型

  • 持久节点(PERSISTENT)
  • 临时节点(EPHEMERAL)—— 在客户端会话结束后自动删除,利用临时节点的这一特性,我们可以使用临时节点来进行集群管理,包括发现服务的上下线等。
  • 持久/顺序节点(PERSISTENT_SEQUENTIAL)
  • 临时/顺序节点(EPHEMERAL_SEQUENTIAL)

顺序节点 —— 每次创建顺序节点时,zk都会在路径后面自动添加上10位的数字(计数器),例如 0000000001,0000000002,……这个计数器可以保证在同一个父节点下是唯一的。在zk内部使用了4个字节的有符号整形来表示这个计数器,也就是说当计数器的大小超过2147483647时,将会发生溢出。

节点数据

节点可以存储数据,zk保证读和写都是原子操作,且每次读写操作都是对数据的完整读取或完整写入,并不提供对数据进行部分读取或者写入的操作。

但是你不应该在节点存储过多的数据。zk规定节点的数据大小不能超过1M,但实际上我们在znode的数据量应该尽可能小,因为数据过大会导致zk的性能明显下降。如果确实需要存储大量的数据,一般解决方法是在另外的分布式数据库(例如redis)中保存这部分数据,然后在znode中我们只保留这个数据库中保存位置的索引即可。

数据读写原子性、不易存储大量数据

节点属性

  • dataVersion —— 数据版本号,每次对节点进行set操作,dataVersion的值都会增加1(即使设置的是相同的数据)。
  • cversion —— 子节点的版本号。当znode的子节点有变化时,cversion 的值就会增加1。
  • aclVersion —— ACL的版本号,关于znode的ACL(Access Control List,访问控制)。
  • cZxid —— Znode创建的事务id。
  • mZxid —— Znode被修改的事务id,即每次对znode的修改都会更新mZxid。
  • ctime —— Znode创建的时间戳。
  • mtime —— Znode修改的时间戳。
  • dataLength —— 数据长度。
  • numChildren —— 子节点数量。
  • ephemeralOwner —— 如果znode是ephemeral类型节点,则这是znode所有者的 session ID。 如果znode不是ephemeral节点,则该字段设置为零。

版本号 —— 以数据版本号(dataVersion)来说明zk中版本号的作用。每一个znode都有一个dataVersion,它随着每次数据变化而自增。ZooKeeper提供的一些API例如setData和delete根据版本号有条件地执行。多个客户端对同一个znode进行操作时,版本号的使用就会显得尤为重要。例如,假设客户端C1对znode /config写入一些配置信息,如果另一个客户端C2同时更新了这个znode,此时C1的版本号已经过期,C1调用setData一定不会成功。这正是版本机制有效避免了数据更新时出现的先后顺序问题。在这个例子中,C1在写入数据时使用的版本号无法匹配,使得操作失败。

多客户端操作zk节点数据图示

事务ID —— 对于zk来说,每次的变化都会产生一个唯一的事务id,zxid(ZooKeeper Transaction Id)。通过zxid,可以确定更新操作的先后顺序。例如,如果zxid1小于zxid2,说明zxid1操作先于zxid2发生。zxid对于整个zk都是唯一的,即使操作的是不同的znode。

zkid在客户端重连服务器时的应用

在集群模式下,客户端有多个服务器可以连接,当尝试连接到一个不同的服务器时,这个服务器的状态要与最后连接的服务器的状态要保持一致。zk正是使用zxid来标识这个状态,上图描述了客户端在重连情况下zxid的作用。

  1. 客户端连接Server1
  2. 客户端创建了一个节点,这时候Server1的zxid变成了100
  3. 客户端与Server1断开了连接
  4. 客户端连接Server2,此时Server2的zxid小于100,导致连接失败
  5. 客户端连接S3,S3的zxid大于100,连接成功