一、前言
想起很久以前在某个客户现场,微服务 B 突然无法调用到微服务 A,为了使服务尽快正常恢复,重启了微服务 B 。
但客户不依不饶询问这个问题出现的原因,于是我还大老远从杭州飞到深圳,现场排查问题。
最后的结论是,zk 在某时刻出现主备切换,此时微服务 A(基于 dubbo)需要重新往 zk上注册,但是端口号变了。
但是微服务 B 本地有微服务 A rpc 接口的缓存,缓存里面还是旧的端口,所以调用不到。
解决方法就是,把微服务的 rpc 端口号改成固定的。
虽说原因找到了,但对于 Zookeeper 的理解还是不够深刻,于是重新学习了 Zookeeper 的核心设计,并记录于此文共勉。
二、Zookeeper 核心架构设计
1、Zookeeper 特点
(1)Zookeeper 是一个分布式协调服务,是为了解决多个节点状态不一致的问题,充当中间机构来调停。如果出现了不一致,则把这个不一致的情况写入到 Zookeeper 中,Zookeeper 会返回响应,响应成功,则表示帮你达成了一致。
比如,A、B、C 节点在集群启动时,需要推举出一个主节点,这个时候,A、B、C 只要同时往 Zookeeper 上注册临时节点,谁先注册成功,谁就是主节点。
(2)Zookeeper 虽然是一个集群,但是数据并不是分散存储在各个节点上的,而是每个节点都保存了集群所有的数据。
其中一个节点作为主节点,提供分布式事务的写服务,其他节点和这个节点同步数据,保持和主节点状态一致。
(3)Zookeeper 所有节点的数据状态通过 Zab 协议保持一致。当集群中没有 Leader 节点时,内部会执行选举,选举结束,Follower 和 Leader 执行状态同步;当有 Leader 节点时,Leader 通过 ZAB 协议主导分布式事务的执行,并且所有的事务都是串行执行的。
(4)Zookeeper 的节点个数是不能线性扩展的,节点越多,同步数据的压力越大,执行分布式事务性能越差。推荐3、5、7 这样的数目。
2、Zookeeper 角色的理解
Zookeeper 并没有沿用 Master/Slave 概念,而是引入了 Leader,Follower,Observer 三种角色。
通过 Leader 选举算法来选定一台服务器充当 Leader 节点,Leader 服务器为客户端提供读、写服务。
Follower 节点可以参加选举,也可以接受客户端的读请求,但是接受到客户端的写请求时,会转发到 Leader 服务器去处理。
Observer 角色只能提供读服务,不能选举和被选举,所以它存在的意义是在不影响写性能的前提下,提升集群的读性能。
3、Zookeeper 同时满足了 CAP 吗?
答案是否,CAP 只能同时满足其二。
Zookeeper 是有取舍的,它实现了 A 可用性、P 分区容错性、C 地写入一致性,牺牲的是 C的读一致性。
也就是说,Zookeeper 并不保证读取的一定是最新的数据。如果一定要最新,需要使用 sync 回调处理。
三、核心机制一:ZNode 数据模型
Zookeeper 的 ZNode 模型其实可以理解为类文件系统,如下图:
编辑
添加图片注释,不超过 140 字(可选)
1、ZNode 并不适合存储太大的数据
为什么是类文件系统呢?因为 ZNode 模型没有文件和文件夹的概念,每个节点既可以有子节点,也可以存储数据。
那么既然每个节点可以存储数据,是不是可以任意存储无限制的数据呢?答案是否定的。在 Zookeeper 中,限制了每个节点只能存储小于 1 M 的数据,实际应用中,最好不要超过 1kb。
原因有以下四点:
-
同步压力:Zookeeper 的每个节点都存储了 Zookeeper 的所有数据,每个节点的状态都要保持和 Leader 一致,同步过程至少要保证半数以上的节点同步成功,才算最终成功。如果数据越大,则写入的难度也