官方对zk的介绍
在 apache.zookeeper.org 上,可以看到针对Zookeeper的相关描述,官网是这么解释Zookeeper的:
ZooKeeper is a distributed, open-source coordination service for distributed applications. It exposes a simple set of primitives that distributed applications can build upon to implement higher level services for synchronization, configuration maintenance, and groups and naming. It is designed to be easy to program to, and uses a data model styled after the familiar directory tree structure of file systems.
ZooKeeper是一种用于分布式应用程序的分布式开源协调服务。 它公开了一组简单的原语,分布式应用程序可以构建这些原语,以实现更高级别的服务,以实现同步,配置维护以及组和命名。 它被设计为易于编程,并使用类似目录树结构的数据模型。
总结为以下几点:
- 主要用于分布式协调服务,不可用于数据库(每个znode最多存储1M数据)
- 只提供简单的原语,具体的功能由客户端应用程序实现
- 基于类似目录树的树形结构
原语:原语是由若干个机器指令构成的完成某种特定功能的一段程序,具有不可分割性。原语一般都是低级的核心功能,可以通过原语组合完成高级功能。
特性
根据官方文档描述,归纳zk的特性如下:
- 扩展性
- 可用性
- 最终一致性
- 时序性:客户端的更新将按照发送顺序执行
- 原子性,一系列操作要么全都成功,要么同时失败,不可分割
- 可靠性(可用性),zk200ms就会完成Leader的选举
- 及时性,系统客户视图保证在特定事件范围内是最新的
- 统一视图,无论连接到哪台服务器,客户端都会看到相同的服务视图
下面就针对以上特性结合原理进行说明。
原理与特性分析
Observer角色与扩展性
zookeeper集群架构众所周知,共由三种角色组成:
- Leader
- Follwer
- Observer
是一种Master-Slaver架构,其中Leader负责数据的写,Follwer与Observer负责数据的读;其中,Leader与Follwer可以进行投票选举,而Observer是不参与选举的。
zk进行选举时,需要每个结点进行投票,其复杂度为O(n)。所以,当Follwer结点越来越多时,其选举时间也会越来越长。所以zk增加了一个新的角色Observer:
- Observer不参与投票
- Observer提供读取操作
Follwer作为备选人,只需要够用就可以(保证Leader挂了有新的Leader代替)。所以如果想要扩展zookeeper的读性能,只需要直接增加Observer结点即可。相较于增加Follwer结点,因为Observer不参与投票,所以不会增加选举的时间;既有可用性保证,又可横向扩展zk的读能力。所以Observer的设计既增强了zk的可用性,又增加了扩展性。
Paxos 协议
Paxos协议是基于消息传递的一致性算法,Leslie Lamport在1990年提出,被广泛应用于分布式计算中,Google的Chubby,Apache的Zookeeper都是基于它的理论来实现,Paxos还被认为是到目前为止唯一的分布式一致性算法,其它的算法都是Paxos的改进或简化。注意,Paxos有一个前提:没有拜占庭将军问题。
简单来说就是分布式多个结点之间如何同步才能保证数据一致性。
拜占庭将军问题:
拜占庭将军问题(Byzantine failures),是由莱斯利·兰伯特提出的点对点通信中的基本问题。含义是在存在消息丢失的不可靠信道上试图通过消息传递的方式达到一致性是不可能的。因此对一致性的研究一般假设信道是可靠的,或不存在本问题。
拜占庭位于如今的土耳其的伊斯坦布尔,是东罗马帝国的首都。由于当时拜占庭罗马帝国国土辽阔,为了达到防御目的,每个军队都分隔很远,将军与将军之间只能靠信差传消息。 在战争的时候,拜占庭军队内所有将军和副官必须达成一致的共识,决定是否有赢的机会才去攻打敌人的阵营。但是,在军队内有可能存有叛徒和敌军的间谍,左右将军们的决定又扰乱整体军队的秩序。在进行共识时,结果并不代表大多数人的意见。这时候,在已知有成员谋反的情况下,其余忠诚的将军在不受叛徒的影响下如何达成一致的协议,拜占庭问题就此形成。
Paxos流程(以下内容转载并修改自 Zookeeper全解析——Paxos作为灵魂):
Paxos描述了这样一个场景,有一个叫做Paxos的小岛(Island)上面住了一批居民,岛上面所有的事情由一些特殊的人决定,他们叫做议员(Senator)。议员的总数(Senator Count)是确定的,不能更改。岛上每次环境事务的变更都需要通过一个提议(Proposal),每个提议都有一个编号(PID),这个编号是一直增长的,不能倒退。每个提议都需要超过半数((Senator Count)/2 +1)的议员同意才能生效。每个议员只会同意大于当前编号的提议,包括已生效的和未生效的。如果议员收到小于等于当前编号的提议,他会拒绝,并告知对方:你的提议已经有人提过了。这里的当前编号是每个议员在自己记事本上面记录的编号,他不断更新这个编号。整个议会不能保证所有议员记事本上的编号总是相同的。现在议会有一个目标:保证所有的议员对于提议都能达成一致的看法。
好,现在议会开始运作,所有议员一开始记事本上面记录的编号都是0。有一个议员发了一个提议:将电费设定为1元/度。他首先看了一下记事本,嗯,当前提议编号是0,那么我的这个提议的编号就是1,于是他给所有议员发消息:1号提议,设定电费1元/度。其他议员收到消息以后查了一下记事本,哦,当前提议编号是0,这个提议可接受,于是他记录下这个提议并回复:我接受你的1号提议,同时他在记事本上记录:当前提议编号为1。发起提议的议员收到了超过半数的回复,立即给所有人发通知:1号提议生效!收到的议员会修改他的记事本,将1好提议由记录改成正式的法令,当有人问他电费为多少时,他会查看法令并告诉对方:1元/度。
现在看冲突的解决:假设总共有三个议员S1-S3,S1和S2同时发起了一个提议:1号提议,设定电费。S1想设为1元/度, S2想设为2元/度。结果S3先收到了S1的提议,于是他做了和前面同样的操作。紧接着他又收到了S2的提议,结果他一查记事本,咦,这个提议的编号小于等于我的当前编号1,于是他拒绝了这个提议:对不起,这个提议先前提过了。于是S2的提议被拒绝,S1正式发布了提议: 1号提议生效。S2向S1或者S3打听并更新了1号法令的内容,然后他可以选择继续发起2号提议。
活锁问题
上述投票过程还有一个问题:S1 S2 S3 当前的提议编号假设都为1(C1=1, C2=1, C3=1), 并同时发起一个提议(这些提议编号都为2, P1=2, P2=2, P3=2)。
- 假设S1先收到S2的提议后,发现
C1=1 < P1=2
,投同意票,C1++
,此时C1 = 2
- S2收到S3的提议,发现
C2=1 < P3=2
,同意票,C2++
,此时C2 = 2
- S3收到S1的提示,发现
C3=1 < P1=2
,同意票,C3++
,此时C3 = 2
- 接下来,S1、S2、S3分别收到S3、S1、S2的提议,发现
P3=2 !> C1=2
、P1=2 !> C2=2
、P2=2 !> C3=2
,他们的提议编号都是2,都不大于他们的当前编号,所以产生了活锁问题。
解决活锁问题
解决活锁问题,需要在议员中设置总统,只有总统有权发出提议,如果议员有自己的提议,必须发给总统并由总统来提出。
zk Server中的角色都可以与Paxos的角色对应:
- 小岛(Island)——ZK Server Cluster
- 议员(Senator)——ZK Server
- 提议(Proposal)——ZNode Change(Create/Delete/SetData…)
- 提议编号(PID)——Zxid(ZooKeeper Transaction Id)
- 正式法令——所有ZNode及其数据
- 总统——ZK Server Leader
ZAB协议
Zookeeper Atomic Broadcast,zk原子广播协议,基于Paxos,同样用于解决分布式数据一致性问题。
可靠性
zookeeper的可靠性主要表现在:
- Leader Down后,迅速恢复Leader