1
谈谈你对 Zookeeper 的理解?
Zookeeper 是一个开源的分布式协调服务,由雅虎公司创建,由于最初雅虎公司的内部研究小组的项目大多以动物的名字命名,所以后来就以 Zookeeper(动物管理员)来命名了,而就是由 Zookeeper 来负责这些分布式组件环境的协调工作。
他的目标是可以提供高性能、高可用和顺序访问控制的能力,同时也是为了解决分布式环境下数据一致性的问题。
集群
首先,Zookeeper 集群中有几个关键的概念:Leader、Follower 和 Observer。
Zookeeper 中通常只有 Leader 节点可以写入,Follower 和 Observer 都只是负责读,但是 Follower 会参与节点的选举和过半写成功,Observer 则不会,他只是单纯的提供读取数据的功能。
通常这样设置的话,是为了避免太多的从节点参与过半写的过程,导致影响性能,这样 Zookeeper 只要使用一个几台机器的小集群就可以实现高性能了,如果要横向扩展的话,只需要增加 Observer 节点即可。
Zookeeper 建议集群节点个数为奇数,只要超过一半的机器能够正常提供服务,那么整个集群都是可用的状态。
数据节点 Znode
Zookeeper 中数据存储于内存之中,这个数据节点就叫做 Znode,他是一个树形结构,比如/a/b/c 类似。
而 Znode 又分为持久节点、临时节点、顺序节点三大类。
持久节点是指只要被创建,除非主动移除,否则都应该一直保存在 Zookeeper 中。
临时节点不同的是,他的生命周期和客户端 Session 会话一样,会话失效,那么临时节点就会被移除。
还有就是临时顺序节点和持久顺序节点,除了基本的特性之外,子节点的名称还具有有序性。
会话 Session
会话自然就是指 Zookeeper 客户端和服务端之间的通信,他们使用 TCP 长连接的方式保持通信,通常,肯定会有心跳检测的机制,同时他可以接受来自服务器的 Watch 事件通知。
事件监听器 Wather
用户可以在指定的节点上注册 Wather,这样在事件触发的时候,客户端就会收到来自服务端的通知。
权限控制 ACL
Zookeeper 使用 ACL 来进行权限的控制,包含以下 5 种:
CREATE,创建子节点权限
DELETE,删除子节点权限
READ,获取节点数据和子节点列表权限
WRITE,更新节点权限
ADMIN,设置节点 ACL 权限
所以,Zookeeper 通过集群的方式来做到高可用,通过内存数据节点Znode来达到高性能,但是存储的数据量不能太大,通常适用于读多写少的场景。
2
Zookeeper 有哪些应用场景?
命名服务 Name Service,依赖 Zookeeper 可以生成全局唯一的节点 ID,来对分布式系统中的资源进行管理。
分布式协调,这是 Zookeeper 的核心使用了。利用 Wather 的监听机制,一个系统的某个节点状态发生改变,另外系统可以得到通知。
集群管理,分布式集群中状态的监控和管理,使用 Zookeeper 来存储。
Master 选举,利用 Zookeeper 节点的全局唯一性,同时只有一个客户端能够创建成功的特点,可以作为 Master 选举使用,创建成功的则作为 Master。
分布式锁,利用 Zookeeper 创建临时顺序节点的特性。
3
说说 Wather 监听机制和它的原理?
Zookeeper 可以提供分布式数据的发布/订阅功能,依赖的就是 Wather 监听机制。
客户端可以向服务端注册 Wather 监听,服务端的指定事件触发之后,就会向客户端发送一个事件通知。
他有几个特性:
一次性:一旦一个 Wather 触发之后,Zookeeper 就会将它从存储中移除
客户端串行:客户端的 Wather 回调处理是串行同步的过程,不要因为一个 Wather 的逻辑阻塞整个客户端
轻量:Wather 通知的单位是 WathedEvent,只包含通知状态、事件类型和节点路径,不包含具体的事件内容,具体的时间内容需要客户端主动去重新获取数据
主要流程如下:
客户端向服务端注册 Wather 监听;
保存 Wather 对象到客户端本地的 WatherManager 中;
服务端 Wather 事件触发后,客户端收到服务端通知,从 WatherManager 中取出对应 Wather 对象执行回调逻辑。
4
Zookeeper 是如何保证数据一致性的?
Zookeeper 通过 ZAB 原子广播协议来实现数据的最终顺序一致性,他是一个类似 2PC 两阶段提交的过程。
由于 Zookeeper 只有 Leader 节点可以写入数据,如果是其他节点收到写入数据的请求,则会将之转发给 Leader 节点。
主要流程如下:
Leader 收到请求之后,将它转换为一个 proposal 提议,并且为每个提议分配一个全局唯一递增的事务 ID:zxid,然后把提议放入到一个 FIFO 的队列中,按照 FIFO 的策略发送给所有的 Follower
Follower 收到提议之后,以事务日志的形式写入到本地磁盘中,写入成功后返回 ACK 给 Leader
Leader 在收到超过半数的 Follower 的 ACK 之后,即可认为数据写入成功,就会发送 commit 命令给 Follower 告诉他们可以提交 proposal了
ZAB 包含两种基本模式,崩溃恢复和消息广播。
整个集群服务在启动、网络中断或者重启等异常情况的时候,首先会进入到崩溃恢复状态,此时会通过选举产生 Leader 节点,当集群过半的节点都和 Leader 状态同步之后,ZAB 就会退出恢复模式。之后,就会进入消息广播的模式。
5
Zookeeper 如何进行 Leader 选举的?
Leader 的选举可以分为两个方面,同时选举主要包含事务 zxid 和 myid,节点主要包含 LEADING\FOLLOWING\LOOKING 3 个状态。
服务启动期间的选举
服务运行期间的选举
服务启动期间的选举
首先,每个节点都会对自己进行投票,然后把投票信息广播给集群中的其他节点
节点接收到其他节点的投票信息,然后和自己的投票进行比较,首先 zxid 较大的优先,如果 zxid 相同那么则会去选择 myid 更大者,此时大家都是 LOOKING 的状态
投票完成之后,开始统计投票信息,如果集群中过半的机器都选择了某个节点机器作为 leader,那么选举结束
最后,更新各个节点的状态,leader 改为 LEADING 状态,follower 改为 FOLLOWING 状态
服务运行期间的选举
如果开始选举出来的 leader 节点宕机了,那么运行期间就会重新进行 leader 的选举。
leader 宕机之后,非 observer 节点都会把自己的状态修改为 LOOKING 状态,然后重新进入选举流程
生成投票信息(myid,zxid),同样,第一轮的投票大家都会把票投给自己,然后把投票信息广播出去
接下来的流程和上面的选举是一样的,都会优先以 zxid,然后选择 myid,最后统计投票信息,修改节点状态,选举结束
6
那选举之后又是怎样进行数据同步的?
那实际上 Zookeeper 在选举之后,Follower 和 Observer (统称为Learner)就会去向 Leader 注册,然后就会开始数据同步的过程。
数据同步包含 3 个主要值和 4 种形式。
PeerLastZxid:Learner服务器最后处理的 ZXID
minCommittedLog:Leader提议缓存队列中最小 ZXID
maxCommittedLog:Leader提议缓存队列中最大 ZXID
直接差异化同步 DIFF 同步
如果 PeerLastZxid 在 minCommittedLog 和 maxCommittedLog 之间,那么则说明 Learner 服务器还没有完全同步最新的数据。
首先 Leader 向 Learner 发送 DIFF 指令,代表开始差异化同步,然后把差异数据(从 PeerLastZxid 到 maxCommittedLog 之间的数据)提议 proposal 发送给 Learner
发送完成之后发送一个 NEWLEADER 命令给 Learner,同时 Learner 返回 ACK 表示已经完成了同步
接着等待集群中过半的 Learner 响应了 ACK 之后,就发送一个 UPTODATE 命令,Learner 返回 ACK,同步流程结束
先回滚再差异化同步 TRUNC+DIFF 同步
这个设置针对的是一个异常的场景。
如果 Leader 刚生成一个 proposal,还没有来得及发送出去,此时 Leader 宕机,重新选举之后作为 Follower,但是新的 Leader 没有这个 proposal 数据。
举个栗子:
假设现在 Leader 是 A,minCommittedLog=1,maxCommittedLog=3,刚好生成的一个 proposal 的 ZXID=4,然后挂了。
重新选举出来的 Leader是B,B 之后又处理了 2 个提议,然后minCommittedLog=1,maxCommittedLog=5。
这时候 A 的 PeerLastZxid=4,在(1,5)之间。
那么这一条只存在于 A 的提议怎么处理?
A 要进行事务回滚,相当于抛弃这条数据,并且回滚到最接近于 PeerLastZxid 的事务,对于 A 来说,也就是 PeerLastZxid=3。
流程和 DIFF 一致,只是会先发送一个 TRUNC 命令,然后再执行差异化 DIFF 同步。
仅回滚同步 TRUNC 同步
针对 PeerLastZxid 大于 maxCommittedLog 的场景,流程和上述一致,事务将会被回滚到 maxCommittedLog 的记录。
这个其实就更简单了,也就是你可以认为 TRUNC+DIFF 中的例子,新的Leader B 没有处理提议,所以 B 中 minCommittedLog=1,maxCommittedLog=3。
所以 A 的 PeerLastZxid=4 就会大于 maxCommittedLog 了,也就是 A 只需要回滚就行了,不需要执行差异化同步 DIFF 了。
全量同步 SNAP 同步
适用于两个场景:
PeerLastZxid 小于 minCommittedLog
Leader 服务器上没有提议缓存队列,并且 PeerLastZxid 不等于 Leader 的最大 ZXID
这两种场景下,Leader 将会发送 SNAP 命令,把全量的数据都发送给 Learner 进行同步。
7
有可能会出现数据不一致的问题吗?
还是会存在的,我们可以分成 3 个场景来描述这个问题。
查询不一致
因为 Zookeeper 是过半成功即代表成功,假设我们有 5 个节点,如果 123 节点写入成功,如果这时候请求访问到 4 或者 5 节点,那么有可能读取不到数据,因为可能数据还没有同步到 4、5 节点中,也可以认为这算是数据不一致的问题。
解决方案可以在读取前使用 sync 命令。
leader 未发送 proposal 宕机
这也就是数据同步说过的问题。
leader 刚生成一个 proposal,还没有来得及发送出去,此时 leader 宕机,重新选举之后作为 follower,但是新的 leader 没有这个 proposal。
这种场景下的日志将会被丢弃。
leader 发送 proposal 成功,发送 commit 前宕机
如果发送 proposal 成功了,但是在将要发送 commit 命令前宕机了,如果重新进行选举,还是会选择 zxid 最大的节点作为 leader,因此,这个日志并不会被丢弃,会在选举出 leader 之后重新同步到其他节点当中。
8
如果作为注册中心,Zookeeper 和 Eureka、Consul、Nacos 有什么区别?
9
你对于 CAP 理论怎么理解?
CAP 是一个分布式系统设计的定理,他包含 3 个部分,并且最多只能同时满足其中两个。
Consistency 一致性,因为在一个分布式系统中,数据肯定需要在不同的节点之间进行同步,就比如 Zookeeper,所以一致性就是指的是数据在不同的节点之间怎样保证一致性,对于纯理论的 C 而言,默认的规则是忽略掉延迟的,因为如果考虑延迟的话,因为数据同步的过程无论如何都会有延迟的,延迟的过程必然会带来数据的不一致。
Availability 可用性,这个指的是对于每一个请求,节点总是可以在合理的时间返回合理的响应,比如 Zookeeper 在进行数据同步时,无法对外提供读写服务,不满足可用性要求。这里常有的一个例子是说 Zookeeper 选举期间无法提供服务不满足 A,这个说法并不准确,因为 CAP 关注的是数据的读写,选举可以认为不在考虑范围之内。所以,可以认为对于数据的读写,无论响应超时还是返回异常都可以认为是不满足 A。
Partition-tolerance 分区容错性,因为在一个分布式系统当中,很有可能由于部分节点的网络问题导致整个集群之间的网络不连通,所以就产生了网络分区,整个集群的环境被分隔成不同的的子网,所以,一般说网络不可能 100%的不产生问题,所以 P 一定会存在。
为什么只能同时满足 CAP 中的两个呢?
以 A\B 两个节点同步数据举例,由于 P 的存在,那么可能 AB 同步数据出现问题。
如果选择 AP,由于 A 的数据未能正确同步到 B,所以 AB 数据不一致,无法满足 C。
如果选择 CP,那么 B 就不能提供服务,就无法满足 A。
结束!