Zookeeper 使用了一种称为 Zab(Zookeeper Atomic Broadcast)的协议作为其一致性的核心。Zab 协议是 Paxos 协议的一种变形,下面将展示一些协议的核心内容。
考虑到 Zookeeper 的主要操作数据状态,为了保证一致性,Zookeeper 提出了两个安全属性:
- 全序(Total Order):如果消息 A 在消息 B 之前发送,则所有 Server 应该看到相同结果。
- 因果顺序(Causal Order):如果消息 A 在消息 B 之前发生(A 导致了 B),并且一起发送,则消息 A 始终在消息 B 之前被执行。
为了保证上述两个安全属性,Zookeeper 使用了 TCP 协议和 Leader。通过使用 TCP 协议保证了消息的全序的特性(先发先到),通过 Leader 解决了因果顺序(先到 Leader 先执行)。因为有了 Leader,Zookeeper 的架构就变成为:Master-Slave 模式,但在该模式中 Master(Leader)会 Crash,因此,Zookeeper 引入 Leader 选举算法,以保证系统的健壮性。
当 Zookeeper Server 收到写操作,Follower 会将其转发给 Leader,由 Leader 执行操作。Client 可以直接从 Follower 上读取数据,如果需要读取最新数据,则需要从 Leader 节点读取,Zookeeper 设计的读写比大致为 2:1。
Leader 执行写操作可以简化为一个两段式提交的 transaction:
- Leader 发送 proposal 给所有的 Follower。
- 收到 proposal 后,Follower 回复 ACK 给 Leader,接受 Leader 的 proposal.
- 当 Leader 收到大多数的 Follower 的 ACK 后,将 commit 其 proposal。
在这个过程中,proposal 的确认不需要所有节点都同意,如果有 2n+1 个节点,那么只要有 n 个节点同意即可,也就是说 Zookeeper 允许 n 个节点 down 掉。任何两个多数派必然有交集,在 Leader 切换(Leader down)时,这些交集依然保持着最新的系统状态。如果集群节点个数少于 n+1 个时,Zookeeper 将无法进行同步,也就无法继续工作。
Zab 与 Paxos
Zab 的作者认为 Zab 与 paxos 并不相同,只所以没有采用 Paxos 是因为 Paxos 保证不了全序顺序:
Because multiple leaders can propose a value for a given instance two problems arise. First, proposals can conflict. Paxos uses ballots to detect and resolve conflicting proposals. Second, it is not enough to know that a given instance number has been committed, processes must also be able to figure out which value has been committed.
举个例子。假设一开始 Paxos 系统中的 Leader 是 P1,他发起了两个事务{t1, v1}(表示序号为 t1 的事务要写的值是 v1)和{t2, v2},过程中 Leader 挂了。新来个 Leader 是 P2,他发起了事务{t1, v1'}。而后又来个新 Leader 是 P3,他汇总了一下,得出最终的执行序列{t1, v1'}和{t2, v2}。
这样的序列为什么不能满足 ZooKeeper 的需求呢?ZooKeeper 是一个树形结构,很多操作都要先检查才能确定能不能执行,比如 P1 的事务 t1 可能是创建节点“/a”,t2 可能是创建节点“/a/aa”,只有先创建了父节点“/a”,才能创建子节点“/a/aa”。而 P2 所发起的事务 t1 可能变成了创建“/b”。这样 P3 汇总后的序列是先创建“/b”再创建“/a/aa”,由于“/a”还没建,创建“a/aa”就搞不定了。
为了保证这一点,ZAB 要保证同一个 leader 的发起的事务要按顺序被 apply,同时还要保证只有先前的 leader 的所有事务都被 apply 之后,新选的 leader 才能在发起事务。