一般设计一个分布式协同服务都会提供一组primitives的列表,对于每个primitive暴露一些接口来创建实例,比如分布式锁可以作为一个primitive, 它暴露了一些接口如create, acquire, release等。这种设计会存在一些问题:1)扩展性差,需要提供大量的primitives,还要不断扩展API; 2)灵活性差,对于不同应用适用性会差的较多

Zookeeper的设计并没有直接暴露primitives,而是暴露了一组类似操作文件系统的API来帮助不同应用构建自己的primitives。Zookeeper允许操作 小的数据节点,被称为znodeznode按照树的层级关系组织起来,如下图所示:

Zookeeper的znode中的数据是以byte array形式存储的,byte arrays的具体格式(如Protocol Buffers, Thrift, Avr)是由具体应用自己指定的, Zookeeper本身不支持对各种格式parse。

Zookeeper支持的API包括:

  • create /path data
  • delete /path
  • get /path
  • set /path data
  • ls /path
  • stat /path

Note: 具体API操作说明可以通过help命令查看

ZNode的模式

  • persistent
    • create命令默认创建的模式,只有通过delete操作才能删除掉
  • ephemeral
    • 可以通过create -e命令创建,显示delete或连接session过期都能删除掉
  • persistent_sequential
    • 可以通过create -s命令创建,如连续三次执行create -s /tasks/task- ““会创建task-0000000000task-0000000001task-0000000002三个节点
  • ephemeral_sequential
    • 可以通过create -e -s命令创建

Watch

Zookeeper的client如何知道znode的数据或状态发生变化呢?Zookeeper采用notification的机制,允许client通过对znode设置一个watch来注册接收 notification。Watch是一次性的,一个watch只能接收一次notification。如下图所示:

client每次收到notification之后都需要设置一个新的watch,这期间znode可能会发生新的变化,有人会觉得新的变化会被client错过,但实际上每次 设置watch都会伴随着一次读操作,所以不会错过任何状态变化。

Version

有些API操作需要client带上znode的version作为参数,只有version与server当前的version一致时候,操作才能生效,这样确保在并发操作的情况下 正确性,如图所示:

Zookeeper架构

Zookeeper整体架构如下图所示:

Zookeeper Quorum

quorum是需要参加投票的最少的server个数,也是确保client数据安全保存了的最小server数目。 Zookeeper集群server的数量最好是奇数。

Sessions

Client提交到zookeeper的所有操作都是关联到某一个session的。当session结束时,session内创建的所有ephemeral节点全部被删除。 同一session内的API操作能确保FIFO。client建立的session如果失去了与当前server的连接,client library能确保client重新与其它server建立连接, 但仍沿用同一个session id。