目录
[两大核心过程:选举 Leader 和 写数据同步。](#两大核心过程:选举 Leader 和 写数据同步。)
[选举 Leader](#选举 Leader)
[ZAB 写数据同步](#ZAB 写数据同步)
一般特性
Zookeeper 是一个分布式协调中间件,使得分布式系统中各个节点达成共识,完成数据一致性,数据同步,配置管理,服务发现等功能。
CAP:Zookeeper 在 CAP 理论中只满足 CP ,即满足强一致性,分区容错性,不满足高可用性。它在 Leader 选举区间不可用,在发生网络分区期间如果 Leader 节点所在分区的节点个数不超过集群总节点个数一半时不可用。
主要作用:在 Hadoop ,Kafka 这些早期的大数据生态主要做集群选主的分布式协调中间件,在 Java 中做分布式锁,在 Dubbo 等 RPC 框架中做注册和配置中心,Watch 机制可以监控配置数据变化,也可以存储少量数据。
缺点:性能一般,因为强一致性要求集群中只有一个 Leader 节点,只有 Leader 节点可以写入数据,写入数据时需要多数节点投票同意。
使用协议:使用 ZAB (ZooKeeper Atomic Broadcast) 原子广播协议,在 Leader 选举,写数据时事务同步起作用。ZAB 协议就是 Raft 协议的变体,分布式协调中间件比如 Etcd,Consul 都是使用 Raft 协议。
ZAB 有 3 大目标:原子性,顺序性,一致性。原子性:创建节点和修改数据等事务操作在所有节点中要么全部成功,要么全部识别。 顺序性:所有事务根据 ZXID 事务 id 严格排序,确保集群所有节点对事务顺序形成共识。 一致性:集群所有节点访问到的状态数据全都一致。
Zookeeper 中的主要角色:Leader ,Follow,Observer。Leader 是主节点,集群中只有一个,创建节点,修改数据等事务操作只有它才能做。Follow 是从节点,可以读数据,在启动时或崩溃恢复时选举 Leader 节点。Observer 可有可无,它不参与选举 Leader 节点,只负责同步 Leader 节点的数据。集群节点最少是3个,最好是奇数个。
重要接口:QuorumPeer,Learner 。QuorumPeer 是仲裁节点接口,拥有这个接口的节点就可以参与投票选举 Leader,Leader 和 Follow 都继承它,是集群节点的抽象;Learner 是数据同步接口,从 Leader 节点同步数据,是 Follow 和 Observer 的抽象。
两大核心过程:选举 Leader 和 写数据同步。
选举 Leader
Leader 节点会和所有的 Follow 节点保持定时心跳,默认 200毫秒,当第一个 Follow 节点发现 Leader 节点失联时,它会把自己的状态从 Following 切换为 Looking 并向其他节点发起选举提议,触发选举流程。如果此时 Leader 节点确实下线了,那么其他节点就会陆续进入 Looking 状态。第一轮投票每个节点都选自己,同时设置好本节点 id 和 zxid 事务 id 广播给其他节点,各个节点比较 zxid 的大小,选出 zxid 最大(zxid 更大则数据更新),如果 zxid 相同 ,选择节点 id 比较大的节点广播给其他节点,如果某个节点发现自己的票数超过了总节点数一半,然后就把自己设置为 Leading 状态,发出广播携带 epoch 新任期号和最新的 zxid 通知结束选举,同时和 Follow 节点建立 TCP 连接,保持心跳,同步数据。 如果之前第一次发现 Leader 失联只是因为偶发网络延迟导致的,其他节点还是和 Leader 保持心跳连接,那么这个选举提议就会被忽略,Leader 还是 Leader,如果不久后该 Follow 和 Leader 恢复联系的话就恢复之前的状态。
ZAB 写数据同步
Leader 节点收到客户端创建,删除节点和修改数据等写操作请求时,会生成一个 zxid ,即集群全局事务id(格式为 epoch + counter,epoch 是 Leader 任期,counter 是递增序号,确保事务顺序性)。 Leader 将该提议广播给 Follow 和 Observer,Follow 写到磁盘上的事务日志,并返回 ACK ,Observer 也会持久化该日志,但不会返回 ACK ,因为它不参与任何投票。Leader 收到过半节点 (包括 Leader 节点1个) 的 ACK 后广播 Commit 指令,要求提交事务。Follower/Observer 收到 Commit 指令后,将事务从本地事务日志加载到内存中的数据树(Data Tree),完成数据更新。Leader 节点给客户端返回成功。 这个过程是原子性的,要么成功要么失败。这个过程中 Leader 节点本身也会持久化事务,可以对外提供读请求,Leader 节点和Follow,Observer 节点的数据都是一致的。若 Leader 在广播过程中故障,未提交的事务会被新 Leader 丢弃;若 Follower 未收到 Commit 指令(如网络中断),恢复后会主动向 Leader 同步缺失的事务,确保数据最终一致。
常见问题
脑裂
Zookeeper 发生网络分区时,不会产生脑裂问题,因为必须满足过半节点共识原则。Leader 选举必须过半,否则选不出来;Leader 写操作必须有过半的 ACK 进行才能提交事务。比如 11 个节点的集群分裂为 2 个有 5 个节点和 6 个节点的网络分区,那么只有 6 个节点的分区才能提交事务,5个节点的分区不行,因为它不满足过半节点共识原则,6 个节点的分区如果有原来的 Leader 节点那就继续提供服务,如果没有的话就重新选举出 Leader 节点继续服务。如果那6 个节点的分区再次分裂成2个子分区,那么这个 Leader就不能正常服务了,它也很快会发现自己不能和其他的5个 Follow 节点正常通信,它会自动放弃 Leader 身份,进入 Looking 状态参与选举。
节点故障下线
Zookeeper 集群中某个节点下线了,那么总的集群个数还是不变,如果下线的节点是 Follow 节点,那么后续的 ZAB 消息广播的多数派阈值依旧还是之前的数值;如果下线的节点是 Leader 节点,那么会触发 Leader 选举,多数派阈值依旧还是之前的数值;除非运维人员把Zookeeper 配置文件 zoo.cnf 修改了,手动删除了下线节点的信息并重启 Zookeeper,滚动更新才可以修改 Zookeeper 中总的集群节点。总之,集群节点个数是按照 Zookeeper 配置文件计算出来的。
还有其他的问题,比如 Leader 选举超时导致写消息服务不可用;Leader 广播写消息但没收到 Follow 的 ACK 就宕机导致数据丢失;网络分区后客户端连接到的是少数派分区,导致读到的是旧数据,也不能写数据,要做健康检测;Zookeeper 日志过多,磁盘满;Zookeeper 内存数据过多导致 OOM;客户端连接过多导致连接资源耗尽......等等