关于zookeeper:深入了解Zookeeper核心原理

28次阅读

共计 6235 个字符,预计需要花费 16 分钟才能阅读完成。

之前的文章 Zookeeper 根底原理 & 利用场景详解中将 Zookeeper 的基本原理及其利用场景做了一个具体的介绍,尽管介绍了其底层的存储原理、如何应用 Zookeeper 来实现分布式锁。然而我认为这样也仅仅只是理解了 Zookeeper 的一点 皮毛 而已。所以这篇文章就给大家具体聊聊 Zookeeper 的 外围底层原理。不太熟悉 Zookeeper 的能够回过头去看看。

ZNode

这个应该算是 Zookeeper 中的根底,数据存储的最小单元。在 Zookeeper 中,相似文件系统的存储构造,被 Zookeeper 形象成了树,树中的每一个节点(Node)被叫做 ZNode。ZNode 中保护了一个数据结构,用于记录 ZNode 中数据更改的 版本号 以及ACL(Access Control List)的变更。

有了这些数据的 版本号 以及其更新的Timestamp,Zookeeper 就能够验证客户端申请的缓存是否非法,并协调更新。

而且,当 Zookeeper 的客户端执行 更新 或者删除操作时,都必须要带上要批改的对应数据的版本号。如果 Zookeeper 检测到对应的版本号不存在,则不会执行这次更新。如果非法,在 ZNode 中数据更新之后,其对应的版本号也会 一起更新

这套版本号的逻辑,其实很多框架都在用,例如 RocketMQ 中,Broker 向 NameServer 注册的时候,也会带上这样一个版本号,叫DateVersion

接下来咱们来具体看一下这个保护版本号相干数据的数据结构,它叫Stat Structure,其字段有:

字段 释义
czxid 创立该节点的 zxid
mzxid 最初一次批改该节点的 zxid
pzxid 最初一次批改该节点的子节点的 zxid
ctime 从以后 epoch 开始到该节点被创立,所距离的毫秒
mtime 从以后 epoch 开始到该节点最初一次被编辑,所距离的毫秒
version 以后节点的改变次数(也就是版本号)
cversion 以后节点的子节点的改变次数
aversion 以后节点的 ACL 改变次数
ephemeralOwner 以后长期节点 owner 的 SessionID(如果不是长期节点则为空)
dataLength 以后节点的数据的长度
numChildren 以后节点的子节点数量

举个例子,通过 stat 命令,咱们能够查看某个 ZNode 中 Stat Structure 具体的值。

对于这里的 epoch、zxid 是 Zookeeper 集群相干的货色,前面会具体的对其进行介绍。

ACL

ACL(Access Control List)用于管制 ZNode 的相干权限,其权限管制和 Linux 中的相似。Linux 中权限 品种 分为了三种,别离是 执行 ,别离对应的字母是 r、w、x。其权限粒度也分为三种,别离是 拥有者权限 群组权限 其余组权限,举个例子:

drwxr-xr-x  3 USERNAME  GROUP  1.0K  3 15 18:19 dir_name

什么叫 粒度 ?粒度是对权限所作用的对象的分类,把下面三种粒度换个说法形容就是 对用户(Owner)、用户所属的组(Group)、其余组(Other)的权限划分,这应该算是一种权限管制的规范了,典型的三段式。

Zookeeper 中尽管也是三段式,然而两者对粒度的划分存在区别。Zookeeper 中的三段式为Scheme、ID、Permissions,含意别离为权限机制、容许拜访的用户和具体的权限。

Scheme 代表了一种权限模式,有以下 5 种类型:

  • world 在此中 Scheme 下,ID只能是anyone,代表所有人都能够拜访
  • auth 代表曾经通过了认证的用户
  • digest 应用用户名 + 明码来做校验。
  • ip 只容许某些特定的 IP 拜访 ZNode
  • X509 通过客户端的证书进行认证

同时权限品种也有五种:

  • CREATE 创立节点
  • READ 获取节点或列出其子节点
  • WRITE 能设置节点的数据
  • DELETE 可能删除子节点
  • ADMIN 可能设置权限

同 Linux 中一样,这个权限也有缩写,举个例子:

getAcl办法用户查看对应的 ZNode 的权限,如图,咱们能够输入的后果呈三段式。别离是:

  • scheme 应用了 world
  • id 值为anyone,代表所有用户都有权限
  • permissions 其具体的权限为 cdrwa,别离是CREATE、DELETE、READ、WRITE 和ADMIN 的缩写

Session 机制

理解了 Zookeeper 的 Version 机制,咱们能够持续摸索 Zookeeper 的 Session 机制 了。

咱们晓得,Zookeeper 中有 4 种类型的节点,别离是长久节点、长久程序节点、长期节点和长期程序节点。

在之前的文章咱们聊到过,客户端如果创立了长期节点,并在之后断开了连贯,那么所有的长期节点就都会被 删除 。实际上 断开连接 的谈话不是很准确,应该是说客户端建设连贯时的 Session 过期 之后,其创立的所有长期节点就会被全副删除。

那么 Zookeeper 是怎么晓得哪些长期节点是由以后客户端创立的呢?

答案是 Stat Structure 中的 ephemeralOwner(长期节点的 Owner) 字段

下面说过,如果以后是 长期程序节点 ,那么ephemeralOwner 则存储了创立该节点的 Owner 的 SessionID,有了 SessionID,天然就能和对应的客户端匹配上,当 Session 生效之后,能力将该客户端创立的所有长期节点 全副删除

对应的服务在创立连贯的时候,必须要提供一个带有所有服务器、端口的字符串,单个之间 逗号相隔,举个例子。

127.0.0.1:3000:2181,127.0.0.1:2888,127.0.0.1:3888

Zookeeper 的客户端收到这个字符串之后,会从中随机选一个服务、端口来建设连贯。如果连贯在之后断开,客户端会从字符串中抉择下一个服务器,持续尝试连贯,直到连贯胜利。

除了这种最根本的 IP+ 端口,在 Zookeeper 的 3.2.0 之后的版本中还反对连贯串中带上门路,举个例子。

127.0.0.1:3000:2181,127.0.0.1:2888,127.0.0.1:3888/app/a

这样一来,/app/a就会被当成以后服务的根目录,在其下创立的所有的节点路经都会带上前缀/app/a。举个例子,我创立了一个节点/node_name,那其残缺的门路就会为/app/a/node_name。这个个性特地实用于多租户的环境,对于每个租户来说,都认为本人是最顶层的根目录/

当 Zookeeper 的客户端和服务器都建设了连贯之后,客户端会拿到一个 64 位的 SessionID 和明码。这个明码是干什么用的呢?咱们晓得 Zookeeper 能够部署多个实例,如果客户端断开了连贯又和另外的 Zookeeper 服务器建设了连贯,那么在建设连贯使就会带上这个明码。该明码是 Zookeeper 的一种安全措施,所有的 Zookeeper 节点都能够对其进行验证。这样一来,即便连贯到了其余 Zookeeper 节点,Session 同样无效。

Session过期 有两种状况,别离是:

  • 过了指定的生效工夫
  • 指定工夫内客户端没有发送心跳

对于第一种状况,过期工夫 会在 Zookeeper 客户端建设连贯的时候传给服务器,这个过期工夫的范畴目前只能在 2 倍 tickTime 和 20 倍 tickTime 之间。

ticktime 是 Zookeeper 服务器的配置项,用于指定客户端向服务器发送心跳的距离,其默认值为 tickTime=2000,单位为 毫秒

而这套 Session 的过期逻辑由 Zookeeper 的服务器保护,一旦 Session 过期,服务器会 立刻删除 由 Client 创立的所有长期节点,而后 告诉 所有正在监听这些节点的客户端相干变更。

对于第二种状况,Zookeeper 中的心跳是通过 PING 申请 来实现的,每隔一段时间,客户端都会发送 PING 申请到服务器,这就是心跳的实质。心跳使服务器感知到客户端还活着,同样的让客户端也感知到和服务器的连贯依然是无效的,这个距离就是tickTime,默认为 2 秒。

Watch 机制

理解完 ZNode 和 Session,咱们终于能够来持续下一个要害性能 Watch 了,在下面的内容中也不止一次的提到 监听(Watch)这个词。首先用一句话来概括其作用

给某个节点注册监听器,该节点一旦产生变更(例如更新或者删除),监听者就会收到一个 Watch Event

和 ZNode 中有多种类型一样,Watch 也有多种类型,别离是一次性 Watch 和永久性 Watch。

  • 一次性 Watch 在被触发之后,该 Watch 就会移除
  • 永久性 Watch 在被触发之后,依然保留,能够持续监听 ZNode 上的变更,是 Zookeeper 3.6.0 版本新增的性能

一次性的 Watch 能够在调用 getData()getChildren()exists()等办法时在参数中进行设置,永久性的 Watch 则须要调用 addWatch() 来实现。

并且一次性的 Watch 会 存在问题,因为在 Watch 触发的事件达到客户端、再到客户端设立新的 Watch,是有一个工夫距离的。而如果在这个工夫距离中产生的变更,客户端则无奈感知。

Zookeeper 集群架构

ZAB 协定

把后面的都铺垫好之后就能够来从整体架构的角度再深刻理解 Zookeeper。Zookeeper 为了保障其 高可用 ,采纳的基于主从的 读写拆散 架构。

咱们晓得在相似的 Redis 主从架构中,节点之间是采纳的 Gossip 协定来进行通信的,那么在 Zookeeper 中通信协议是什么?

答案是 ZAB(Zookeeper Atomic Broadcast) 协定。

ZAB 协定是一种 反对解体复原 的的 原子播送 协定,用于在 Zookeeper 之间传递音讯,使所有的节点都放弃同步。ZAB 同时具备高性能、高可用的、容易上手、利于保护的特点,同时反对主动的故障复原。

ZAB 协定将 Zookeeper 集群中的节点划分成了三个角色,别离是 LeaderFollowerObserver,如下图:

总的来说,这套架构和 Redis 主从或者 MySQL 主从的架构相似(感兴趣的也能够去看之前的写的文章,都有聊过)

  • Redis 主从
  • MySQL 主从

不同点在于,通常的主从架构中存在两种角色,别离是 Leader、Follower(或者是 Master、Slave),但 Zookeeper 中多了一个 Observer。

那问题来了,Observer 和 Follower 的区别是啥呢?

实质上来说两者的性能是一样的,都为 Zookeeper 提供了横向扩大的能力,使其可能扛住更多的并发。但区别在于 Leader 的选举过程中,Observer不参加投票选举

程序一致性

上文提到了 Zookeeper 集群中 是读写拆散 的,只有 Leader 节点能解决写申请,如果 Follower 节点接管到了写申请,会将该申请转发给 Leader 节点解决,Follower 节点本身是不会解决写申请的。

Leader 节点接管到音讯之后,会依照申请的严格程序一一的进行解决。这是 Zookeeper 的一大特点,它会保障音讯的 程序一致性

举个例子,如果音讯 A 比音讯 B 先到,那么在所有的 Zookeeper 节点中,音讯 A 都会先于音讯 B 达到,Zookeeper 会保障音讯的 全局程序

zxid

那 Zookeeper 是如何保障音讯的程序?答案是通过zxid

能够简略的把 zxid 了解成 Zookeeper 中音讯的惟一 ID,节点之间会通过发送 Proposal(事务提议) 来进行通信、数据同步,proposal 中就会带上 zxid 和具体的数据(Message)。而 zxid 由两局部组成:

  • epoch 能够了解成朝代,或者说 Leader 迭代的版本,每个 Leader 的 epoch 都不一样
  • counter 计数器,来一条音讯就会自增

这也是惟一 zxid 生成算法的底层实现,因为每个 Leader 所应用的 epoch 都是惟一的,而不同的音讯在雷同的 epoch 中,counter 的值是不同的,这样一来所有的 proposal 在 Zookeeper 集群中都有惟一的 zxid。

恢复模式

失常运行的 Zookeeper 集群会处于 播送模式 。相同,如果超过半数的节点宕机,就会进入 恢复模式

什么是恢复模式?

在 Zookeeper 集群中,存在两种模式,别离是:

  • 恢复模式
  • 播送模式

当 Zookeeper 集群故障时会进入 恢复模式 ,也叫做 Leader Activation,顾名思义就是要在此阶段 选举出 Leader。节点之间会生成 zxid 和 Proposal,而后互相投票。投票是要有准则的,次要有两条:

  • 选举进去的 Leader 的 zxid 肯定要是所有的 Follower 中最大的
  • 并且已有超过半数的 Follower 返回了 ACK,示意认可选举进去的 Leader

如果在选举的过程中产生异样,Zookeeper 会间接进行新一轮的选举。如果一切顺利,Leader 就会被胜利选举进去,然而此时集群还不能失常对外提供服务,因为新的 Leader 和 Follower 之间还没有进行要害的 数据同步

尔后,Leader 会期待其余的 Follower 来连贯,而后通过 Proposal 向所有的 Follower 发送其缺失的数据。

至于怎么晓得缺失哪些数据,Proposal 自身是要记录日志,通过 Proposal 中的 zxid 的低 32 位的 Counter 中的值,就能够做一个 Diff

当然这里有个优化,如果缺失的数据太多,那么一条一条的发送 Proposal 效率太低。所以如果 Leader 发现缺失的数据过多就会将以后的数据 打个快照,间接打包发送给 Follower。

新选举进去的 Leader 的 Epoch,会在原来的值上 +1,并且将 Counter 重置为 0。

到这你是不是认为就完了?实际上到这还是无奈失常提供服务

数据同步 实现之后,Leader 会发送一个 NEW_LEADER 的 Proposal 给 Follower,当且仅当该 Proposal 被过半的 Follower 返回 Ack 之后,Leader 才会 Commit 该 NEW_LEADER Proposal,集群能力失常的进行工作。

至此,恢复模式 完结,集群进入 播送模式

播送模式

在播送模式下,Leader 接管到音讯之后,会向其余所有 Follower 发送Proposal(事务提议),Follower 接管到 Proposal 之后会返回 ACK 给 Leader。当 Leader 收到了 quorums 个 ACK 之后,以后 Proposal 就会提交,被利用到节点的内存中去。quorum 个是多少呢?

Zookeeper 官网倡议每 2 个 Zookeeper 节点中,至多有一个须要返回 ACK 才行,假如有 N 个 Zookeeper 节点,那计算公式应该是n/2 + 1

这样可能不是很直观,用 大白话 来说就是,超过半数的 Follower返回了 ACK,该 Proposal 就可能提交,并且利用至内存中的 ZNode。

Zookeeper 应用 2PC 来保障节点之间的数据一致性(如上图),然而因为 Leader 须要跟所有的 Follower 交互,这样一来通信的开销会变得较大,Zookeeper 的性能就会降落。所以为了 晋升 Zookeeper 的 性能 ,才从所有的 Follower 节点返回 ACK 变成了 过半的 Follower 返回 ACK即可。

好了以上就是本篇博客的全部内容了,欢送微信搜寻关注【SH 的全栈笔记 】,回复【 队列】获取 MQ 学习材料,蕴含根底概念解析和 RocketMQ 具体的源码解析,继续更新中。

如果你感觉这篇文章对你有帮忙,还麻烦 点个赞 关个注 分个享 留个言

正文完
 0