ZKEssentials读书笔记(二) - 18965050/ZookeeperEssentials GitHub Wiki
ZK数据模型类似于Unix的文件系统(filesystem).每个节点在zk中被称为znode,里面可以存储数据
- znode中的数据以字节(byte)的方式存储,大小限制不能大于1M
- 目前znode路径(path)只能以绝对路径方式表示, 不支持相对路径(chroot 这个特性在zk 3.2.0版本中新增了.这样, 不同的用户可以指定不同的根目录)
- znode节点还包含版本号,创建,修改时间,访问控制列表(ACL)等信息. 当节点数据变更时,版本号会相应的增加
znode有两种类型(每种类型又有有序,无序之分),在节点创建时指定
- 持久化的znode创建之后,必须手动才能删除. 示例如下:
create /hello world get /hello
- 持久化znode一般用作数据存储,配置管理
-
临时znode在会话结束,客户端或服务端崩溃的情况下会删除. 当然,也可以手动删除
-
临时znode不能拥有子节点
create -e /hello world create -e /hello/child aaa Ephemerals cannot have children: /hello/child
-
临时znode的这种特性非常适合用作成员管理(member management)
-
zk对于有序节点会自动在节点名称后添加序号(十位数字,单向递增)
create -s /hello world create -e -s /hello/world
-
有序znode的这种特性适合用作分布式队列或锁的实现
-
zk client在对znode操作时可注册Watch.这样,当此znode发生变化(比如数据变化,节点子节点变化等)时,所有注册的Watch都会收到相应的事件,进行处理
-
Watch是一次性的. 当触发后需要继续监听相应的事件, 需要再次注册Watch
-
znode触发watch的事件包括:
- 节点数据变化.通过getData操作注册. 触发操作为setData,事件为
Watch.Event.EventType.NodeDataChanged
- 子节点发生变化.通过getChildren操作注册. 触发操作为子节点的create,delete,事件为
Watch.Event.EventType.NodeChildrenChanged
- 节点本身变化.通过exists操作注册. 触发操作为节点的create,delete事件为
Watch.Event.EventType.NodeCreated
和Watch.Event.EventType.NodeDeleted
- 节点数据变化.通过getData操作注册. 触发操作为setData,事件为
-
ZK以先进先出(FIFO)的方式严格保证注册Watch的触发顺序(比如两个Watch注册了某个事件, ZK保证Watch触发顺序为Watch注册顺序)
-
ZK保证事件在通知到client watch之前,此节点不会再有其他的变化(比如,zk保证节点不会发生变化,如果前一个数据变化事件还未通知到Watch)
-
ZK保证Watch事件的顺序(比如某个znode进行了数据修改和节点变化,zk保证此两个操作的事件顺序)
-
由于watch是一次性触发的. 在重新复位监听的过程中可能会丢失节点变化事件. 这点在开发过程中需要注意
-
如果client从server断开并重新连接, 不管连接到的是原先的server还是一个新的server, 注册过的watch都会被重新注册并触发(断开期间触发的事件将会被顺序的通知到Watch).但有一种情况除外, 就是client监听某个还未创建znode,在client断开期间, 此znode被创建并删除了, client重新连接后将不会收到任何事件
一个成员管理示例图见下:
ZK的ACL类似于Unix系统中的权限控制, 但其没有拥有者(ownership)的概念
- World: 任何人都可以访问ZK服务
- Auth: 用户认证
- Digest:用户名/密码认证
- IP Address:IP地址认证
ZK也支持插件形式的自定义认证机制
ZK ACL不具有传递性, 即不能传递给子节点
ZK中, 每个ACL有认证机制, 身份ID和对应的权限三部分组成.ZK中已定义了一些默认的ACL:
ZK znode状态结构可通过 stat
或 ls2
命令查看.
- cZxid: 节点创建时的事务ID
- mZxid: 节点修改时的事务ID
- pZxid: 节点子节点变化(增加, 删除)时的事务ID
- ctime: 节点创建时的时间. 单位:毫秒
- mtime: 节点修改时的时间. 单位:毫秒
- dataVersion: 节点数据变化版本号
- cversion: 节点子节点变化版本号
- aclVersion: 节点ACL变化版本号
- ephemeralOwner: 临时节点创建时的会话ID. 如果不是临时节点, 此值为0
- dataLength: 节点数据长度
- numChildren: 节点子节点数量
zk集群有个定额(quorum)的概念,数据存储或维持集群可用性必须要达到定额的数量. 定额数量由公式 total_num%2+1
j计算. 比如一个有5台机器组成的ZK集群,定额数量是3, 而由6台机器组成的ZK集群,定额数量是4. 这也是为什么ZK集群一般由奇数服务组成.
- zk client和server连接, 会生成一个会话. 会话id为64位的数字.
- client连接server,可以设置会话超时时长(SESSION_TIMEOUT),单位毫秒.在会话超时时长内, client会向server发心跳检测, server会重置client的超时时长. 如果由于网络拥塞或断开等原因,导致会话超时,会话就会过期无效了(expired). 因此对于网络情况不太好的环境或大量机器组成的集群,建议SESSION_TIMEOUT设置长些
- 会话状态流程图见下:
- ZK集群中的服务器分为领导者(leader)和追谁者(follower). 所有更改操作(修改数据, 增删节点等操作)都由leader发起,由followers进行投票决定.而所有读取请求(getData, exists等操作)均直接从follower获取, 以保证速度
- ZK集群内部使用ZAB(ZooKeeper Atomic Broadcasting)协议进行消息的广播. ZAB协议是事务的, 确保要不一起成功, 或一起失败
- ZK中的每个事务都需要经过两个流程: 选举领导者和原子广播
- 选举领导者: 每个参与选举的server 节点进入LOOKING状态, 如果已经选举出领导者,则对端节点会相互通知领导者是谁,并进入FOLLOWING状态.而领导者进入LEADING状态; 如果未选举出领导者,这通过选举协议进行领导者选举,直至某个服务节点胜出.选举协议消息包含服务器的id(sid)和最近的事务id(zxid),领导者由最大的zxid对应的sid担当, 其余变为follower
- 原子广播: 更新事务请求会发往领导者, 领导者根据ZAB协议发送更新请求到每个follower. 只有达到定额数量追谁者同意此请求,事务才会被提交
- ZK机器节点处理领导者和追随者之外, 还有一类节点:观察着.观察着和追谁者类似,除了没有投票选举的权利.追谁者是为了提升读请求(read request)而出现的
- 事务提交后,会被追加(append)到事务日志. 在saving point点会异步产生快照. 事务日志存放目录由dataLogDir参数配置, 数据目录由dataDir配置