大厂开始放弃ZooKeeper,还用学吗?
共 17412字,需浏览 35分钟
·
2024-11-14 08:45
👉目录
1 ZooKeeper,时代弃子?
2 ZooKeeper 核心通识
3 典型应用场景
11 月 14 日晚 8 点,腾讯云开发者视频号揭秘《北大人在鹅厂写代码有神器?》,预约看看大佬在工作中都有哪些“秘密武器”!观看直播还有机会抢鹅厂周边好礼!
01
为了应对大流量,现代应用/中间件通常采用分布式部署,此时不得不考虑 CAP 问题。ZooKeeper(后文简称 ZK)是面向 CP 设计的一个开源的分布式协调框架,将那些复杂且容易出错的分布式一致性服务封装起来,构成一个高效可靠的原语集,并以一系列简单易用的接口提供给用户使用,分布式应用程序可以基于它实现诸如数据发布/订阅、负载均衡、命名服务、集群管理、Master 选举、分布式锁、分布式队列等功能。
但最近几年,业界却逐渐发现,互联网大厂纷纷放弃了对 ZooKeeper 的选型,一时引起争议无数。不仅如此,与 ZooKeeper 捆绑甚深的 Kafka 也在 2.8 版本里宣布了放弃 ZooKeeper,而在之前的版本中,没有 ZooKeeper 甚至无法运行。
随着云原生技术的兴起和分布式系统规模的扩大,Zookeeper在处理大规模集群和快速扩展时的性能瓶颈日益显现。这是它开始被逐渐放弃、替代的原因之一。
从技术的角度来看,历史的车轮在不断向前滚动,学术界和工业界的理论基础一直在不断进化,技术也要适应不断革新的业务不停去演进。但是合适的组件总会出现在合适的地方,这就是架构师和研发人员的工作和责任,也是我们应该继续去学习 ZooKeeper 的原因。
02
public class DataNode implements Record {
byte data[];
Long acl;
public StatPersisted stat;
private Set<String> children = null;
}
-
data:znode 相关的业务数据均存储在这里,但是,父节点不可存储数据; -
children:存储当前节点的子节点引用信息,因为内存限制,所以 znode 的子节点数不是无限的; -
stat:包含 znode 节点的状态信息,比如: 事务 id、版本号、时间戳等,其中事务 id 和 ZK 的数据一直性、选主相关,下面将重点介绍; -
acl:记录客户端对 znode 节点的访问权限;
-
epoch:任期/纪元,Zxid 的高32位, ZAB 协议通过 epoch 编号来区分Leader 周期变化,每次一个 leader 被选出来,它都会有一个新的 epoch=(原来的 epoch+1),标识当前属于那个leader的 统治时期;可以假设 leader 就像皇帝,epoch 则相当于年号,每个皇帝都有自己的年号; -
事务计数器:Zxid 的低32位,每次数据变更,计数器都会加一;
-
PERSISTENT:永久节点 -
EPHEMERAL:临时节点 -
PERSISTENT_SEQUENTIAL:永久顺序节点 -
EPHEMERAL_SEQUENTIAL:临时顺序节点
-
watcher 变更通知是一次性的:当数据发生变化的时候, ZK 会产生一个 watcher 事件,并且会发送到客户端。但是客户端只会收到一次通知。如果后续这个节点再次发生变化,那么之前设置 Watcher 的客户端不会再次收到消息。可以通过循环监听去达到永久监听效果。 -
客户端 watcher 顺序回调:watcher 回调是顺序串行化执行的,只有回调后客户端才能看到节点最新的状态。watcher 回调逻辑不应太复杂,否则可能影响 watcher 执行。 -
不会告诉节点变化前后的具体内容:watchEvent 是最小的通信单元,结构上包含通知状态、事件类型和节点路径,但是,不会告诉节点变化前后的具体内容。 -
时效性:watcher 只有在当前 session 彻底失效时才会无效,若在 session 有效期内快速重连成功,则 watcher 依然存在,仍可收到事件通知。
-
Leader:一个 ZK 集群同一时间只会有一个实际工作的 Leader,它会发起并维护与各 Follwer 及 Observer 间的心跳。所有的写操作必须要通过 Leader 完成再由 Leader 将写操作广播给其它服务器。 -
Follower:一个 ZK 集群可同时存在多个 Follower,它会响应 Leader 的心跳。Follower 可直接处理并返回客户端的读请求,同时会将写请求转发给 Leader 处理,参与事务请求 Proposal 的投票及 Leader 选举投票。 -
Observer:Observer 是3.3.0 版本开始引入的一个服务器角色,一个 ZK 集群可同时存在多个 Observer, 功能与 Follower 类似,但是,不参与投票。
-
LOOKING:寻找 Leader 状态。当服务器处于该状态时,它会认为当前集群中没有 Leader,因此需要进入 Leader 选举状态。 -
LEADING:领导者状态。表明当前服务器角色是 Leader。 -
FOLLOWING:跟随者状态,同步 leader 状态,参与投票。表明当前服务器角色是 Follower。 -
OBSERVING:观察者状态,同步 leader 状态,不参与投票。表明当前服务器角色是 Observer。
-
客户端向 Leader 发起写请求。 -
Leader 将写请求以 Proposal 的形式发给所有 Follower 并等待 ACK。 -
Follower 收到 Leader 的 Proposal 后返回 ACK。 -
Leader 得到过半数的 ACK(Leader 对自己默认有一个 ACK)后向所有的 Follower 和 Observer 发送 Commmit。 -
Leader 将处理结果返回给客户端。
-
Leader 不需要得到所有 Follower 的 ACK,只要收到过半的 ACK 即可,同时Leader 本身对自己有一个ACK。上图中有2个 Follower,只需其中两个返回ACK即可,因为(1+1) / (2+1) > 1/2。 -
Observer 虽然无投票权,但仍须同步 Leader 的数据从而在处理读请求时可以返回尽可能新的数据。
-
客户端向 Follower 发起写请求, Follower 将写请求转发给 Leader 处理; -
其它流程与直接写 Leader 无任何区别。
-
第一阶段:Leader数据写入事件作为提案广播给所有 Follower 结点;可以写入的Follower结点返回确认信息 ACK。 -
第二阶段:Leader 收到一半以上的 ACK 信息后确认写入可以生效,向所有结点广播 COMMIT 将提案生效。
-
Leader 每发起一个提案,会将提案的 ZXID 和内容放到 outstandingProposals 中,作为待提交的提案; -
Leader收到 Follower 的 ACK 信息后,根据 ACK 中的 ZXID 从 outstandingProposals 中找到对应的提案,对 ACK 计数; -
执行 tryToCommit 尝试将提案提交:判断流程是,先判断当前 ZXID 之前是否还有未提交提案,如果有,当前提案暂时不能提交;再判断提案是否收到半数以上 ACK,如果达到半数则可以提交;如果可以提交,将当前 ZXID 从 outstandingProposals 中清除并向 Followers 广播提交当前提案;
-
服务器 ID(myid):每个 ZooKeeper 服务器,都需要在数据文件夹下创建一个名为 myid 的文件,该文件包含整个 ZooKeeper 集群唯一的 ID(整数)。该参数在选举时如果无法通过其他判断条件选择 Leader,那么将该 ID 的大小来确定优先级。 -
事务 ID(zxid):单调递增,值越大说明数据越新,权重越大。 -
逻辑时钟(epoch-logicalclock):同一轮投票过程中的逻辑时钟值是相同的,每投完一次值会增加。 -
ZK 的 leader 选举存在两类,一个是服务器启动时 leader 选举,另一个是运行过程中服务器宕机时的 leader 选举,下面依次展开介绍。
-
Server1 收到 Server2 和 Server3 的广播选票后,由于 logicClock 和 zxid 都相等,此时就比较 myid; -
Server1 收到的两张选票中 Server3 的 myid 最大,此时 Server1 判断应该遵从 Server3 的投票决定,将自己的票改投给 Server3。接下来 Server1 先清空自己的票箱(票箱中有第一步中投给自己的选票),然后将自己的新投票(1->3)和接收到的 Server3 的(3->3)投票一起存入自己的票箱,再把自己的新投票决定(1->3)广播出去,此时 Server1 的票箱中有两票:(1->3),(3->3); -
同理,Server2 收到 Server3 的选票后也将自己的选票更新为(2->3)并存入票箱然后广播。此时 Server2 票箱内的选票为(2->3),(3->3); -
Server3 根据上述规则,无须更新选票,自身的票箱内选票仍为(3->3); -
Server1 与 Server2 重新投给 Server3 的选票广播出去后,由于三个服务器最新选票都相同,最后三者的票箱内都包含三张投给服务器 3 的选票。
-
假死:由于心跳超时(网络原因导致的)认为 Leader 死了,但其实 Leader 还存活着。 -
脑裂:由于假死会发起新的Leader选举,选举出一个新的 Leader,但旧的 Leader 网络又通了,导致出现了两个 Leader ,有的客户端连接到老的 Leader,而有的客户端则连接到新的 Leader。
03
-
当集群中的服务启动时,客户端向 ZK 注册 watcher 监听特定节点,并从节点拉取数据获取配置信息; -
当发布者变更配置时,节点数据发生变化,ZK 会发送 watcher 事件给各个客户端;客户端在接收到 watcher 事件后,会从该节点重新拉取数据获取最新配置信息。
-
服务提供者 server 启动时在 ZK 进行服务注册(创建临时文件); -
服务消费者 client 启动时,请求 ZK 获取最新的服务存活列表并注册 watcher,然后将获得服务列表保存到本地缓存中; -
client 请求 server 时,根据自己的负载均衡算法,从服务器列表选取一个进行通信; -
若在运行过程中,服务提供者出现异常或人工关闭不能提供服务,临时节点失效,ZK 探测到变化更新本地服务列表并异步通知到服务消费者,服务消费者监听到服务列表的变化,更新本地缓存。
-
所有 Dubbo 相关的数据都组织在 /dubbo 的根节点下; -
二级目录是服务名,如 com.foo.BarService ; -
三级目录有两个子节点,分别是 providers 和 consumers ,表示该服务的提供者和消费者; -
四级目录记录了与该服务相关的每一个应用实例的 URL 信息,在 providers 下的表示该服务的所有提供者,而在 consumers 下的表示该服务的所有消费者。举例说明, com.foo.BarService 的服务提供者在启动时将自己的 URL 信息注册到 /dubbo/com.foo.BarService/providers 下;同样的,服务消费者将自己的信息注册到相应的 consumers 下,同时,服务消费者会订阅其所对应的 providers 节点,以便能够感知到服务提供方地址列表的变化。
📢📢欢迎加入腾讯云开发者社群,享前沿资讯、大咖干货,找兴趣搭子,交同城好友,更有鹅厂招聘机会、限量周边好礼等你来~
(长按图片立即扫码)