这是我参与2022首次更文挑战的第17天,活动详情查看:2022首次更文挑战
数据模型data model
- Zookeeper数据模型采用层次化的多叉树结构:
- 最上层根节点以 / 表示,每个节点可以拥有N个子节点
- 每个节点上都可以存储数据,数据类型可以是数字,字符串和二进制序列
- 每个节点称为znode, 是Zookeeper中数据的最小单元,并且每一个znode都有一个唯一的路径标识
- 注意:
- Zookeeper是用来协调服务的,而不是用来存储业务数据的
- 不能在节点znode上存放大量的数据
- Zookeeper中每个节点znode存放数据量的上限是1M
数据节点znode
- Zookeeper中的每一个数据节点称为znode, 是Zookeeper中数据的最小单元
- 数据节点znode的特点:
- 树形结构的每一个节点就是子目录项,称作znode, 这个节点znode使用所在的路径作为唯一标识
- 节点znode可以有子节点目录,每一个节点znode都可以存储数据,默认存储1M的数据.注意临时EPHEMERAL类型的目录节点不能有子节点目录
- 节点znode有版本version, 每个节点znode中存储的数据可以有多个版本.也就是说,一个访问路径中可以存储多份数据,版本号version自动递增
- 节点znode可以被监控,监控这个目录节点中存储的数据的变化以及子节点目录的变化等,如果发生变化就可以通知设置监控的客户端
- 节点znode中每次对Zookeeper状态的改变都会产生一个zxid(Zookeeper Transaction Id),这个zxid是全局有序的,如果zxid1小于zxid2,就说明zxid1中状态的改变在zxid2中状态的改变之前发生
数据节点znode的四种类型
- 持久节点PERSISTENT : 一旦创建节点znode, 就会一直存在.即使Zookeeper宕机也不会消失,直到将该节点znode删除
- 客户端与Zookeeper断开连接后,这个节点还会存在.如果创建成功,就不会意外丢失,即使服务器全部重启还会存在
- 每个持久节点PERSISTENT中既可以包含数据,也可以包含子节点
- 临时节点EPHEMERAL : 临时节点znode的生命周期是与客户端会话session绑定的,会话消失则节点消失. 临时节点znode只能作为叶子节点,不能创建子节点
- 客户端与Zookeeper断开连接后,这个节点就删除
- 临时节点EPHEMERAL在创建的客户端和服务器间的Session结束时自动删除,因为服务器重启会导致Session结束,此时临时节点EPHEMERAL也会自动删除
- 持久顺序节点PERSISTENT_SEQUENTIAL : 除了具备持久节点PERSISTENT的性质之外,节点znode的名称还具有顺序性
- 临时顺序节点EPHEMERAL_SEQUENTIAL : 除了具备临时节点EPHEMERAL的性质之外,节点znode的名称还具有顺序性
数据节点znode的数据结构
- 每个znode由两部分组成:
- data: 节点存放的数据信息
- stat: 节点状态信息
- stat中状态信息描述:
| stat值 | 说明 |
|---|---|
| cZxid | create ZXID 数据节点创建时的事务ID |
| ctime | create time 数据节点的创建时间 |
| mZxid | modified ZXID 数据节点最终一次更新时的事务ID |
| mtime | modified time 数据节点最终一次更新时的时间 |
| pZxid | 数据节点的子节点列表最后一次修改时的事务ID 只有子节点列表变更时才会更新pZxid,子节点内容变更时不会更新 |
| cversion | 子节点版本号 当前数据节点的子节点每次变化时值增加1 |
| dataVersion | 数据节点内容版本号 数据节点创建时值为0,之后每次调用更新数据节点内容操作后值增加1 |
| aclVersion | 数据节点ACL版本号 表示该节点ACL信息变更次数 |
| ephemeralOwner | 创建当前临时节点的客户端会话的sessionId 如果当前节点是持久节点,那么ephemeralOwner=0 |
| dataLength | 数据节点数据内容长度 |
| numChildren | 当前数据节点子节点数量 |
- 示例:
get /dubbo
# data. 数据节点关联的数据信息为空
null
# stat. 数据节点的状态信息
cZxid = 0x2
ctime = Thu Jan 28 09:35:57 CST 2021
mZxid = 0x2
mtime = Thu Jan 28 09:35:57 CST 2021
pZxid = 0x3
cversion = 1
dataVersion = 0
aclVersion = 0
ephemeralOwner = 0x0
dataLength = 0
numChildren = 1
版本version
- Zookeeper中节点znode的stat中记录了节点相关的三个版本信息:
- 当前节点的子节点版本号cversion
- 当前节点的数据内容版本号dataVersion
- 当前节点的ACL版本号aclVersion
权限控制acl
- Zookeeper采用ACL(AccessControlLists) 策略来进行权限控制,类似与UNIX文件系统的权限控制
- 数据节点znode的操作包含以下5种权限:
- CREATE: 创建子节点
- READ: 读取节点的数据和列出子节点
- WRITE: 更新节点的数据
- DELETE: 删除子节点
- ADMIN: 设置节点ACL的权限
- 这里的CREATE和DELETE的权限控制是针对子节点的权限控制
- Zookeeper中znode的四种身份认证的方式:
- world: 默认方式. 所有的用户都可以无条件进行访问
- ip: 指定ip的用户可以进行访问
- auth: 已经进行认证的用户可以进行访问
- digest: 通过用户名username和密码password的认证方式进行访问
事件监听器watcher
- 事件监听器Watcher :
- Zookeeper中一个很重要的特性,是一种监听通知机制
- Zookeeper允许用户在指定的节点znode上注册一些Watcher, 并且在一些特定事件触发时 ,Zookeeper会将事件通知到监听的客户端中
- Zookeeper所有的读操作,比如getData(),getChildren() 和exist() 都可以附带一个watch. 一旦相应的数据发生变化,这个watch就会触发
- 事件监听机制是Zookeeper实现分布式协调服务的重要特性
- watch具有以下几个特点:
- 主动推送 : watch触发时 ,Zookeeper服务器Server主动将更新推送给客户端Client, 不需要客户端Client进行轮询
- 一次性 : 相应数据发生变化时 ,watch只会触发一次.如果客户端Client想要获得后续更新的通知,必须要在watch触发后重新注册一个watch
- 可见性 : 客户端Client在请求中附带watch, 在watch触发的同时再次读取数据,那么在客户端Client得到watch消息之前肯定不可能看到更新后的数据.也就是说,更新通知一定先于更新结果
- 顺序性 : 当存在多个更新触发多个watch时,那么watch触发的顺序和更新的顺序一致
事件监听器的三个关键点
一次性触发one-time trigger
- Zookeeper允许用户在指定的节点注册一些事件监听器watcher, 当节点znode发生变化时,将触发并删除一个watch
- 监视事件可以看作是一次性触发器:
- 在watch触发时客户端会接收到一个数据包,指示节点znode已经发生修改
- 如果客户端Client和Zookeeper服务器Server的连接中断,客户端Client将会接收到本地通知
- 一次性触发机制是Zookeeper实现分布式协调服务的重要特性
- Zookeeper的3.6.0版本中新增功能: 客户端Client可以在节点znode设置永久性递归监视,这些监视在触发时不会删除,并且会已递归的方式触发已注册节点znode以及子节点znode的修改
发送至客户端sent to the client
- 监视事件通过socket异步发送至客户端Client监视者
- 保序性 : Zoookeeper中提供ordering guarantee保序性
- 客户端Client只有把首先看到监视事件之后,才会感知到自身设置监视的节点znode发生变化
- 网络延迟或者其余因素都可能会导致不同的客户端在不同时刻感知到某一个监视事件,但是不同客户端所看到的一切一定都具有一致的顺序
为watch设置的数据the data for which the watch was set
- Zookeeper中维护两条监视链:
- 数据监视data watches : getData() 和exist() 设置数据监视
- 子节点监视child watches : getChildren() 设置子节点监视
- 节点znode本身具有不同的改变方式,改变的可以是节点znode, 改变的也可以是子节点znode:
- getData() 和exist() 设置数据监视. 返回的是节点znode的相关信息
- getChildren() 设置子节点监视. 返回的是子节点znode列表
- 为watch设置数据:
- 数据设置成功后 ,setData() 会触发设置在某一节点znode上设置的数据监视
- 一次成功的create() 操作会触发当前节点znode上设置的数据监视以及父节点的子节点znode监视
- 一次成功的delete() 操作将会触发当前节点znode的数据监视和子节点znode监视事件,同时也会触发当前节点的父节点znode监视
- exists() 丢失监听:
- Zookeeper中的监视是轻量级的,便于设置,维护和分发
- 客户端Client和Zookeeper服务器Server失去连接时,客户端不会接收到监视事件的通知
- 客户端Client重新连接后,在必要的情况下,之前注册的监视器会重新注册并触发,这一过程通常来说是透明的
- 监视事件只有在如下情况才会丢失:
- 通过exists() 设置某个节点znode的监视,如果某个客户端Client在这个节点znode创建和删除的时间间隔内和Zookeeper服务器Server失去连接
- 这种情况下,这个客户端Client即使重新连接Zookeeper服务器后也不会得到监视事件的通知
- Zookeeper中的监视是轻量级的,便于设置,维护和分发
事件监视器工作原理
- 事件监视器Watcher机制中包括三个角色:
- 客户端Client
- 客户端的WatchManager
- Zookeeper服务器Server
- 事件监听器Watcher机制就是Watcher机制中三个角色之间的交互,交互过程分为三个步骤:
- 注册: 客户端Client向Zookeeper服务器Server注册一个Watcher监听
- 存储: 客户端Client将这个监听信息存储到WatchManager中
- 通知: 当Zookeeper中的节点znode发生变化时,Zookeeper服务器Server通知客户端Client,客户端Client调用相应Watcher对象中的回调方法
- 客户端Client创建一个main 线程
- 在main线程中创建两个线程.一个负责网络连接通信的connect线程,一个负责监听的listener线程
- 负责网络连接通信的connect线程将注册的监听事件发送给Zookeeper服务器Server
- 注册的监听事件存储到Zookeeper的注册监听器列表中
- Zookeeper监听到节点znode的数据或者路径发生变化时,将消息通知给负责监听的listener线程
- listener线程内部回调process() 方法进行处理
会话session
- 会话Session :
- 会话Session是Zookeeper服务器与客户之间的一个TCP长连接
- 通过这个会话Session. 客户端可以和Zookeeper服务器之间:
- 通过心跳检测与服务器保持有效的会话
- 向Zookeeper服务器发送请求并接收响应
- 能够通过连接接收来自服务器的Watcher事件通知
- 会话超时时间sessionTimeout :
- 当服务器压力过大,网络故障或者客户端主动断开连接等原因导致客户端连接断开时,只要在会话超时时间sessionTimeout规定的时间内能够重新连接上集群中任意一台服务器,那么之前创建的会话Session还是有效的
- 会话标识sessionID :
- 在创建客户端会话之前,服务器会为每个客户端分配一个sessionID
- 因为sessionID是Zookeeper会话的一个重要标识 ,Zookeeper中与会话有关的运行机制都是基于这个sessionID的,因此,在整个集群中服务器为客户端分配的sessionID要保证在整个集群中是全局唯一