本文主要讲了ZooKeeper系统在设计和实践上的考量,如wait-free和lock,一致性的选择,系统提供的API以及特定语义上的抉择,这样的trade-off是本文的最大启发。

定位

wait-free,high-performance 的协调分布式应用的系统。通过提供协调原语(特定语义的API与数据模型)支持分布式应用的协调需求。

设计

关键词

ZK定位中的关键词有两个:高性能,分布式应用协调服务

ZK的高性能通过WaitFree设计、多副本本地读、Watch机制实现。

  • WaitFree应该是将请求异步处理来实现的,这样异步处理可能会导致请求重排序,导致状态机和现实的时序不同,所以ZK提供了FIFO Client Order 顺序保证。同时,这样的异步处理有益于数据的batch pipeline处理,进一步提升性能。
  • Watch机制,当znode变化是通知Client更新,避免Client操作本地缓存的开销。
  • 多副本本地读,ZK使用ZAB协议实现数据共识,保证写操作满足linearizability。读请求副本本地读,不走ZAB共识协议,但读请求只满足Serializaility,可能会读到之前的结果,但提升了性能。

分布式应用协调服务指的是,ZK提供的数据模型以及API语义,分布式应用可以自由使用来满足诸如Group Membership,Distributed Lock等协调需求。

数据模型与API

ZK为使用者提供znode数据节点的抽象,数据节点通过分层的命名空间组织。ZK提供了Regular + Ephemeral两种znode的节点的创建,每个节点都存储数据,并通过标准的UNIX文件系统路径访问。

实际上,znodes 不是为通用数据存储设计的。 相反,znodes 映射到客户端应用程序的抽象,通常与用于协调的元数据相对应。

也就是说,使用ZK进行协调时,利用好znode关联的元数据,而不是只将znode当做数据存储。比如,znode 将元数据与时间戳(timestamp)和版本计数器( version counter )关联,客户端可以跟踪对 znode 的更改并根据 znode 的版本执行条件更新。

这套数据模型本质上是一个简化API的文件系统,只支持完整数据的读写。使用者将在ZK提供的语义下实现分布式应用的协调。

Regular 和 Ephemeral 的区别在于Ephemeral可以在Session结束时自动删除。

img

Client通过API与ZK交互,ZK通过Session管理Clinet连接,在Session中Clinet可以观测到反应其操作的状态变化。

CAP

ZK保证CP,比如在选举leader时,会停止服务,直到选举成功之后才会再次对外提供服务。

实现

img

ZK使用多副本实现高可用。

简单来说,ZK上层使用ZAB协议处理写请求,保证多副本更新的线性一致性,本地处理读请求,读请求保证顺序一致性。下层数据状态机保存到ZK集群机器上的Replicated Database(内存)+ WAL上,并定期snapshot。整个内存数据库通过 Fuzzy Snapshot + WAL Replay的方式保证单机Crash Safe以及重启恢复的速度。

Fuzzy Snapshot 的优势在于不阻塞在线请求。

与Client的交互

  • 更新操作会通知并清除相关znode的Watch。
  • 读请求本地进行,通过zxid定义与写请求的偏序关系,只保证顺序一致性,可能会Read Stale。ZK提供了sync操作,通过 sync + read 一定程度上解决了这个问题。
  • 当Client连接新ZK Server时,会对比两者的最大zxid,落后的ZK Server将不会为Client建立Session。
  • Client通过心跳维持Session,Server对请求进行幂等处理。

参考

ZooKeeper Paper

MIT6.824-ZooKeeper FAQ