简介
ZAB 是 ZooKeeper Atomic Broadcast (ZooKeeper 原子广播协议)的缩写,它是特别为 ZooKeeper 设计的崩溃可恢复的原子消息广播算法。ZooKeeper 使用 Leader 群首服务器来接收并处理所有事务请求,并采用 ZAB 协议,将服务器数据的状态变更以事务 Proposal 的形式广播到所有的 Follower 服务器上去。这种主备模型架构保证了同一时刻集群中只有一个服务器广播服务器的状态变更,因此能够很好的处理客户端大量的并发请求。
通过 ZAB 协议提交一个事务非常简单,类似于一个两阶段提交:
- 群首服务器 Leader 向所有 Follower 发送一个 Proposal 消息,假设为 p;
- 当一个 Follower 服务器接收到消息 p 后,会响应一个 ACK 消息,通知群首它已经接受该 Proposal;
- 当收到 仲裁数量 的服务器发送的 ACK 消息后(该仲裁数包括群首自己),群首酒会发送 Commit 消息给所有 Follower 更新状态。
Observer 服务器主要用于集群中数据的读扩展,并不参与群首选举和事务 ACK 仲裁,所以不会出现在 ZAB 协议讨论中
另外,整个广播协议是基于具有 FIFO 特性的 TCP 协议来进行网络通讯的,因此能够很容易地保证消息广播过程中消息接收与发送的顺序性。
zxid
那些会改变 ZooKeeper 状态的客户端请求(create,delete 和 setData)将会转发给群首,也就是 Leader 服务器,群首执行相应的操作,并形成状态的更新,则这些更新的操作我们称之为事务。ZooKeeper 集群以事务方式运行,并确保所有的事务以原子方式被执行,同时不被其他事务所干扰。另外,ZooKeeper 中并不存在类似数据库中的回滚操作,它需要确保事务的每一步操作都互不干扰。
当群首产生一个事务时,就会为该事务分配一个标识符,也就是 zxid。zxid 是一个 long 型(64位)整数,分为两部分:epoch (群首周期) 和 counter (事务计数)。
下面是几个表达式,假设 z 是一个事务标识 zxid,
z = < e, c >
e = epoch(z) 表示群首周期
c = counter(z) 表示事务标识中的事务计数
群首周期 epoch 表示群首序列号,每当老的群首崩溃,新群首的 epoch 递增自老群首 epoch。所以即使同一个服务器,先后两次当选成群首,两次 epoch 也是不一样的。
事务计数 counter 用于同一个群首周期里, 区分不同事务的先后。
所以事务的 zxid 确定了事务的顺序,首先比较 epoch,如果 epoch 一样,则比较 counter。
算法描述
整个 ZAB 协议主要包括崩溃恢复和消息广播两个过程,进一步可以分成三个阶段:发现,同步和广播。发现和同步组成崩溃恢复,遵循 ZAB 协议的每一个服务器,会循环执行这三个阶段,我们将这样一个循环称为一个群首周期。
发现
发现阶段就是群首选举阶段,用于在多个 Follower 中选举出一个 Leader。如果集群中所有的服务器均处于 LOOKING 状态时,这些服务器之间就会进行通信来选举一个群首,这个消息,可以称之为群首选举通知(leader election notification)。选举中胜出的服务器进入 LEADING 状态,其他服务器将会进入 FOLLOWING 状态。
当一个服务器进入 LOOKING 状态,它会给集群中所有服务器发送群首选举通知。群首选举通知中包含该服务器的投票信息 vote,它包含服务器标识 sid(可以在配置文件设置)和 zxid。当一个服务器收到一个投票信息,该服务器酒会根据以下规则修改自己的投票信息:
- 假设 voteSid 和 voteZxid 表示接收到通知中的投票信息,mySid 和 myZxid 表示接收方服务器的当前投票信息;
- 如果 (voteZxid < myZxid) 或者 (voteZxid = myZxid 且 voteId < mySid),那么服务器保留当前自己的投票信息;
- 否则,修改自己的投票信息,将 voteZxid 和 voteSid 复制到服务器当前的投票信息,并发送新一轮投票通知。
简而言之,只有最近一次 zxid 的服务器将赢得选举,这样做可以简化群首崩溃后重新仲裁的流程,因为才崩溃恢复时,ZooKeeper 不需要将事务从 Follower 传到 Leader,而只需要将状态从 Leader 传到 Follower。
当一个服务器接收到仲裁数量的选票都投给某台服务器的话,就表示群首选举成功。如果被选举的群首是自己,则服务器将会开始行使群首角色,进入 LEADING 状态,否则就称为 Follower,并尝试连接被选举的群首服务器。一旦连接成功,Follower 和 Leader 之间将会进行状态同步,同步完成后,Follower 才可以处理新的请求。
同步
选完 Leader 以后,ZooKeeper 就进入状态同步过程。
- Leader 等待 Follower 连接;
- Follower 连接 Leader,将最近的 zxid 发送给 Leader;
- Leader 根据 Follower 的 zxid 确定同步点;
- 如果 Follower 滞后不多,Leader 只需发送缺失的事务
- 如果 Follower 滞后很久,Leader 将发送在代码中被称为 SNAP 的完整快照
- 完成同步后通知 Follower 已经成为 UPTODATE 状态;
- Follower 收到 UPTODATE 消息后,又可以重新接受客户端请求。
流程图如下所示:
状态同步广播
完成同步阶段之后,ZAB 协议就可以正式开始接收客户端新的事务请求,并进行广播流程:
- Leader 接收到客户端新的事务请求后,会生成对应的事务 Proposal,并根据 zxid 的顺序向所有 Follower 发送 Proposal;
- Follower 根据消息接收的先后次序处理这些事务 Proposal,并将他们追加到自己的已处理事务集合中,之后发送 ACK 消息反馈给 Leader;
- Leader 接收到仲裁数量的 Follower 针对事务 Proposal 的 Ack 消息后,就发送事务 Commit 消息给所有 Follower,要求他们提交数据更新状态。
- 当 Follower 接收到来自 Leader 的 Commit 消息后,就会提交事务 Proposal,更新状态。需要注意的是,此时该 Follower 必定已经提交 zxid 更小的其他的事务。
内容来源
从 Paxos 到 ZooKeeper 分布式一致性原理与实践
ZooKeeper 分布式过程协同技术详解
网友评论