简介
ZooKeeper 由雅虎研究院开发,后来捐赠给了 Apache。ZooKeeper 是一个开源的分布式应用程序协调服务器,其为分布式系统提供一致性服务。其一致性是通过基于 Paxos 算法的 ZAB 协议完成的。
其主要功能包括:
配置维护
域名服务
分布式同步
集群管理等
ZOOKEEPER理论基础
一、一致性
顺序一致性
原子性
单一视图
最终一致性
二、Paxos算法
前提:不存在拜占庭将军问题,也就是说信道是通畅的。
算法三种角色
在 Paxos 算法中有三种角色,分别具有三种不同的行为。但很多时候,一个进程可能同 时充当着多种角色。
Proposer:提案者,提议者,proposal的提议者
Acceptor:表决者
Learners:学习者
算法的两个阶段
prepare阶段
accept阶段
通俗来讲,prepare阶段是提案者向系统中的其他表决者发送一个提案,其目的是通过一个全局唯一且自增长的N值来确定在表决者那里这个提案是否是最新的,如果不是最新的,表决者是不会接收这个提案的,这种情况下,提案者可以选择重新提案或者放弃提案。
当提案者发现超过半数的表决者都已经接收了这个提案,将进入accept阶段。提案者会给每个表决者发送他的提案,这个阶段依然还要用与prepare阶段相同的策略来确定表决者那里这个提案还是不是最新的,不是最新的话,表决者将拒绝这个提案。若最终提案者发现超过半数的的表决者通过了这个提案,提案者会向外发送两类信息:
向曾 accept 其提案的表决者发送“可执行数据同步信号”,即让它们执行其曾接收到的提案;
-
向未曾向其发送 accept 反馈的表决者发送“提案 + 可执行数据同步信号”,即让
它们接受到该提案后马上执行。
可以看到,paxos算法通过这种策略,来保证所有角色的一致性。
这里有一个问题:
在一个提案者提交提案的时候,其他提案者也是表决者从而参与表决吗?
活锁问题
死锁
死锁很好理解
活锁
Fast Paxos 算法只允许一个进程提交提案,即其具有对 N 的唯一操作权。该方式解决了 “活锁”问题。
三、ZAB协议
简介
因为为了解决活锁问题,只允许一个进程提交提案,能够提交提案的进程就是leader。leader如果挂了,怎么办呢?ZAB协议就是来解决这种问题的,他是一个转为Zookeeper设计的一种支持崩溃恢复(leader选举)的原子广播协议。
ZAB 协议是 Fast Paxos 算法的一种工业实现算法。但两者的设计目标不太一样。ZAB 协 议主要用于构建一个高可用的分布式数据主从系统(注意:是主从系统,而不是主备,主从意味着系统当中所有的服务器都向外提供服务),例如,Leader 挂了,马上就可以选举出 一个新的 Leader。而 Paxos 算法则是用于构建一个分布式一致性状态机系统,确保系统中各 个节点的状态都是一致的。
三个角色
leader:处理写请求,具有提案权。
follower:处理读请求以及写请求转发,具有选举权,被选举权,表决权。
observer:处理读请求(为了提升系统的吞吐量的扩展服务器),没有任何权利。
系统正常运行时可以把这三个角色名称抽象一下:
learner:observer和follower的统称,从leader处同步数据。
quorumServer:具有表决权的服务器。
三个数据
zxid:64位的数据(Long类型),高32位存储epoch,低32位存储xid。
epoch:每个新leader的时代号,存储在集群当中的所有角色当中。
xid:事务请求的流水号,只增不减。
逻辑时钟:与epoch息息相关的一个数据,在leader选举部分会说。
三种模式
恢复模式:leader崩溃的时候,重新启动会进入恢复模式,分为两个阶段,选举阶段和初始化阶段。
广播模式:leader向外广播epoch,以及事务操作。
同步模式:learner同步leader的事务操作。
四种状态
LOOKING:选举状态
FOLLOWING:Follower正常工作状态
OBSERVING:Observer正常工作状态
LEADING:Leader正常工作状态
在Paxos算法中的prepare和accept阶段中,通过比较N值,来确保每个表决者所接收的提案是最新的,然而在commit的时候却没有再次比较N值,这是一种机会主义,这同样会导致有角色保存了不是最新的提案。
为了解决这种问题,如果表决者在accept阶段N值的比较已经确定了其所接收到的提案仍然是最新的提案,那么这个表决者将被赋予一个状态,这种状态下他将不再去接收其它提案,以保证commit到来时,他所保存的提案是最新的。
这里有个问题:
一个事务请求对应的是一个提案吗?
如果是的话,那么两个事务请求来了,第一个事务请求被当做提案提交,第二个事务请求也被当做提案提交了,此时他们都处于prepare阶段,那么accept阶段的时候拿到第一个事务请求就不进行处理了吗?
我的猜想是,ZAB协议通过四种状态,控制了两个提案同时处于prepare阶段的可能性,从而保证每个请求都能够被正常通过并且commit。
答:后边也可以看到,在更新广播算法中,leader维护了一个请求队列来保证顺序,也就是说zk不允许leader一个事务请求还没有同步完成的时候,就去同步下一个事务请求。
可以看出来,ZAB协议对原始的Paxos算法做了非常多的工业上的实际处理。
初始化广播与更新广播算法
1. 初始化广播算法
leader被选举出来之后,他还只是一个准leader,需要经过初始化广播阶段才能算是真正的leader。初始化广播,就是把leader当中有的而其他learner没有的事务请求同步给其他learner。
这里就涉及到一个问题:
leader是如何知道哪些事务请求是自己有而其他learner没有的呢?
初始化广播算法图解
问题:
这里leader为每一个learner都创建一个队列,这个没明白什么意思,一个队列不行吗?一个队列就能够保证顺序了啊。难道是因为每个learner中的所保存的事务请求不同,leader为了针对每个learner的这种差异性去保存不同的事务请求,才设计了多个队列,恩,极有可能是这个原因。
2. 更新广播算法
当learner收到事务请求要把这个请求转发给leader去处理,然后算过更新广播算法把这个事务请求更新到每一个learner当中。算法图解如下:
这里有两个问题
- fast paxos算法,是只有一个角色可以发请求,表决时,发送到follwer处的请求的zxid一定是比follwer的zxid大的。为什么还要进行第三步的比较?follower直接接受提案进行同步就好了啊。
答:ABA问题。
- 第8步,返回的ACK有什么用?
答:
follower每次在同步数据的时候,会从最大的zxid(也就是本次leader发过来的zxid)开始向下递归,去判断跟leader的每个zxid所对应的数据是否一致,不一致则同步过来,直到发现和leader所对应的数据相同为止。
从这里也能看出,zk的所谓的一致性是 最终一致性 而不是 实时一致性。
Observer的数量问题
Observer不是越多越好,通常是与Follower的数量相同。因为follower一旦变多,将会增加系统的同步压力。
Observer与Follower的异同
Observer与Flollower在功能上基本相同,但是在同步的时候,如果Follower已经同步完成了,那么无论Observer是否同步完成都要结束同步,没有完成同步的Observer将无法对外提供服务。
在Leader当中会对他们两个分别维护两个功能相同列表(4个列表哦),一个是记录所有的observer和follower(all列表),一个是记录完成同步的Observer和Follower(service列表)。
对于没有完成同步的observer我认为通常是因为网络波动的原因所致,所以zk在leader和learner(包括observer和follower)之间设计了一种心跳机制,没有同步完成的observer会通过心跳来连接leader,一旦连接上了就开始同步数据,数据同步完成后返回个leader一个ACK,leader会将其加入service列表,此时Observer就会处于Observing状态,就是能够正常向外部提供服务了。
没有完成同步的Follower不会处于Observing状态,当然也不会在leader的service列表当中。只有处于Observing状态的observer能够向外提供服务,这个状态通过Follower与Leader之间的心跳来进行维护(上边已经说了是怎么进行维护的)。
恢复模式的三个原则
首先,说说进入恢复模式的三种场景。
集群启动,leader与其他learner的连接数没有过半
leader宕机,并且已经向其他learner同步了一些事务请求
leader宕机,没有向其他learner同步事务请求,然后leader又起来了
以上三种场景,每种场景进入恢复模式的时候都会对应一个原则
-
Leader主动出让原则
第一种场景下,leader你都跟其他learner连不上了,你得主动出让自己的learner权利。如果任何一个learner发现自己与leader连不上了就会进入LOOKING状态,当有过半的learner处于LOOKING状态,将会重新选举leader。
-
已处理过的消息不能丢失原则
第二种场景下,会在已经同步完数据的follower中进行leader的选举,选举完成后,新的leader会用与更新广播算法相似的形式来向其他没有完成同步的follower进行数据同步。
同步过程:
follower会先向下递归,找到xid与新leader的xid相同并且内容也相同的项,然后再向上递归进行数据同步,知道同步到最大的xid。
-
被丢弃的事务不能再现
第三种场景下,leader此时已经不再是leader,它只要作为一个follower向新的leader按照之前所说过的同步算法进行数据同步就好了。
leader选举(重点,还要阅读源码)
zk高可用集群的容灾设计
集群的数量的奇偶
7台和8台的集群理论上容灾效果是一样的,因为无论7台还是8台机器要想集群能够正常工作,都是遵循需要至少过半的机器没有宕机的原则,而7台或者8台要求过半的机器不宕机,最多能允许出现宕机的机器数量都是3台。所以容灾能力都一样,这个时候就要考虑,多家一台机器所提升的系统吞吐量是否有必要,有必要则选择7台,没必要就选择8台节省资源。
三机房部署
三个机房每个机房的机器数量都不超过zk集群的一半,这样任何一个机房出现问题,都不会影响集群的正常工作。
CAP定理
CAP 原则又称 CAP 定理,指的是在一个分布式系统中,Consistency(一致性)、Availability (可用性)、Partition tolerance(分区容错性),三者不可兼得。
一致性(C):分布式系统中多个主机之间是否能够保持数据一致的特性。即,当系统数据发生更新操作后,各个主机中的数据仍然处于一致的状态。
可用性(A):系统提供的服务必须一直处于可用的状态,即对于用户的每一个请求,系统总是可以在有限的时间内对用户做出响应。
分区容错性(P):分布式系统在遇到任何网络分区故障时,仍能够保证对外提供满足一致性和可用性的服务。
对于分布式系统,网络环境相对是不可控的,出现网络分区是不可避免的,因此系统必 须具备分区容错性。但其并不能同时保证一致性与可用性。CAP 原则对于一个分布式系统来 说,只可能满足两项,即要么 CP,要么 AP。
ZOOKEEPER技术内幕
数据模型Znode
结点类型
持久节点
持久顺序节点
临时节点
临时顺序节点
结点状态
cZxid:CreatedZxid,表示当前znode被创建时的事务ID
ctime:CreatedTime,表示当前znode被创建的时间
mZxid:ModifiedZxid,表示当前znode最后一次被修改时的事务ID
mtime:ModifiedTime,表示当前znode最后一次被修改时的时间
-
pZxid:表示当前znode的子节点列表最后一次被修改时的事务ID。注意,只能是其子
节点列表变更了才会引起pZxid的变更,子节点内容的修改不会影响pZxid。
cversion:ChildrenVersion,表示子节点的版本号。该版本号用于充当乐观锁。
dataVersion:表示当前znode数据的版本号。该版本号用于充当乐观锁。
aclVersion:表示当前znode的权限ACL的版本号。该版本号用于充当乐观锁。
-
ephemeralOwner:若当前znode是持久节点,则其值为0;若为临时节点,则其值为创
建该节点的会话的SessionID。当会话消失后,会根据SessionID来查找与该会话相关的
临时节点进行删除。
dataLength:当前znode中存放的数据的长度。
numChildren:当前znode所包含的子节点的个数。
ACL
ACL 全称为 Access Control List(访问控制列表),是一种细粒度的权限管理策略,可以针 对任意用户与组进行细粒度的权限控制。zk 利用 ACL 控制 znode 节点的访问权限,如节点 数据读写、节点创建、节点删除、读取子节点列表、设置节点权限等。
Zookeeper 的 ACL 分为三个维度:
授权策略 scheme
授权对象 id
用户权限 permission。
授权策略
即如果用户想要访问zk的结点,要怎样验证他的权限。
在 zk 中最常用的有四种策略
IP
digest
world
super
授权对象id
授权对象指的是权限赋予的用户。不同的授权策略具有不同类型的授权对象。下面是各 个授权模式对应的授权对象 id。
ip
digest
world
Super
Watcher机制
zk 通过 Watcher 机制实现了发布/订阅模式。