论文地址:https://pdos.csail.mit.edu/6.824/papers/zookeeper.pdf
一、zookeeper简介
zookeeper是协调分布式应用的服务,ZooKeeper 是基础架构的一部分,目标是提供一个简单的、高性能的内核,供客户端构建更复杂的协调原语。它在多副本、中心化的服务中,组合了 group messaging, shared registers 和分布式锁服务。ZooKeeper 提供的接口有 wait-free 的 shared registers, 和一个与文件系统 cache 失效相似的事件驱动机制,用于提供一个简单但功能强大的协调服务。
ZooKeeper 接口使实现高性能服务成为可能。除了 wait-free 的属性之外,ZooKeeper 提供了一个每个客户端请求是 FIFO 执行的语义,和改变 ZooKeeper 状态的请求都是 Linearizable 的语义。这些设计决策可以使 ZooKeeper 的读可以读本地服务器,使实现高性能处理请求流水线成为可能。对于目标工作负载,我们显示了2:1到100:1 的读/写请求比例,ZooKeeper 每秒可以处理成千上万的事务。 这种性能使 ZooKeeper 可以被客户端应用程序广泛使用。
zookeeper不是在服务侧实现指定的协调原语,而是暴露API给开发者,让开发者实现自己的原语。
zookeeper提供非阻塞的原语,主要是为了不让慢的或有故障的客户端影响到快的客户端
使用基于leader的zab原子广播协议保证写操作的线性化。
使用本地服务器处理读操作,并且不使用zab也不保证线性读。
客户端缓存数据。
二、zookeeper服务
2.1 服务概述
- zookeeper提供层次结构的命名空间作为数据存储,就像linux的文件系统命名结构一样( /A/B/C )。
- 节点有两种类型
(1)常规类型:client显示的创建和删除
(2)临时类型:client创建后,要么主动删除,要么系统自动删除 - 每个节点都有一个单调递增的序列号,子节点的序列号总是比父节点的要大
- 提供watch机制,不需要poll
数据模型:
- 提供像unix文件系统目录一样的数据结构
- 节点不是设计用于存储数据,它代表的是client的映射,但是它也可以存储一些元信息。
会话:
- 客户端连上zookeeper后会初始化一个会话,会话会关联上一个timeout,如果会话超时或者客户端主动关闭,则会话关闭。
- 会话可以在zookeeper集群间透明的转移并且持久化
2.2 客户端API
zookeeper提供的相关API
- create(path, data, flags): 创建一个名称为path,保存数据data的节点,flags表示选择节点类型:regular、ephemeral
- delete(path, version): 删除指定版本的path节点
- exists(path, watch):返回path节点是否存在,watch设置监听
- getData(path, watch):返回path节点的数据和元信息,watch设置监听
- setData(path, data, version): 写data到path节点,如果version版本存在
- getChildren(path, watch): 返回这个path节点的孩子节点名称
- sync(path):等待该客户端sync操作前,所有未传播到该客户端连接的server的操作执行完成,当前的path被忽略
zookeeper的所有API提供了同步和异步的版本,并且异步的api保证客户端能按序收到回调信息。不使用文件句柄,简化了维护状态。
2.3 zookeeper保证
linearizable write:所有更改状态的操作都是线性化,并且遵循优先级
FIFO client order:对于指定的客户端,所有请求都是按序执行
zookeeper的线性写保证与原始的线性(Linearizability: A correctness condi-tion for concurrent objects)保证有所不同,它是异步线性化(A-linearizability),原始的线性化每个客户端只能有一个未完成的操作,但是zookeeper允许有多个并且都是按序执行。zookeeper允许在每个replica读操作,所以允许服务线性化扩展。
监听功能可以使应用系统在做配置变更时保证数据安全性,由于replica可能存在因此因此可以通过sync 接口在读取配置前先将未完成的请求处理完,保证最后读取到最新数据(sync有点像flush)。
两个其它性质保证:
- 如果majority是可用,那么服务可用
- 如果update成功,那么最后失败的replica也会成功update
2.4 原语使用例子
- 动态配置变更
- Rendezvous(约定,有点像动态配置变更):一批process到指定的节点获取配置
- 组成员管理
- 简单的锁:通过创建一个ephemeral类型的节点,并在process挂掉或主动删除时删除此节点。但是它会有惊群效应。
----解决惊群效应的伪代码----
Lock
1: n - create(1 + "/lock-", EPHEMERAL | SEQUENTIAL)
2: C = getChildren(1, false)
3: if n is lowest znode in C, exit
4: p = znode in C ordered just before n
5: if exists(p, true) wait for watch event
6: goto 2
Unlock
1: delete(n)
- 读写锁:写锁和上面一样,读锁没看懂
- double barrier:完全没看懂
三.zookeeper使用案例
略
四.zookeeper实现
zookeeper整体处理流程图
1.Request proccessor
leader接收到请求后将计算系统的状态,将新的状态转换为transaction。
2.原子广播
zookeeper使用zab来复制数据,zab保证消息顺序到达,并且在2f+1的节点下可以容错f个节点。每个节点支持幂等事务。
3.复制数据
每个节点在内存会有一个副本,在节点启动或恢复时,要从磁盘恢复状态。zookeeper定期保存快照,称为fuzzy snapshot(生成时不会上锁)。
4.客户端-服务端交互
本地处理读请求,每个读请求都有一个zxid,zxid定义了该读请求相对写请求的偏序。由于zookeeper不保证线性读,但是提供了sync接口,在sync完以后保证了sync之前的写操作已经完成。