Zookeeper 如今在分布式架构中应用十分广泛,它作为分布式协调框架在分布式架构中有着举足轻重的地位,本文是主要从以上几个方面对 Zookeeper 常用的知识进行总结。
一 从集中式到分布式架构的演变
架构的演变过程在之前的文章《浅谈网站架构演变》中有所介绍
分布式服务架构:
分布式架构:由多台机器通过网络通信组成,分布式的故障发生频率大。
故障原因:网络问题,多台机器网络通信容易超时,中间有可能断掉造成分区。
网络分区:俗称脑裂。
网络的三态问题:要么连接成功,要么失败,要么超时。
所以分布式主要是网络方面和机器方面的问题,最大问题是网络问题。
传统ACID 、 CAP理论、BASE理论:
传统的集中式架构的事务讨论的是 ACID :
原子性:要么成功,要么失败;
一致性:如果出现异常数据,还是原来的那份
隔离性:各个会话之间是相互独立的
持久性:就是提交后的数据永久保存在磁盘上,不丢失。
CAP理论
Consistency,Availability和Partition Tolerance,即CAP。
(C) 一致性:多台机器拥有相同副本
(A) 可用性:在适合的时间响应客户端
(P) 分区容错性:不能出现网络分区
CAP在分布式架构中是不能都具备的,只能选择两种特性,而由于分布式架构都是集群部署的,所以分区容错性(P)是必须要的,可用性(A)、一致性(C)根据架构不同选择的情况也不同,Zookeeper 是CP 模型,也就是说 Zookeeper 在集群出现问题时不会保证可用性,而是会牺牲部分对外服务时间,它保证的是数据的强一致性。
BASE理论
分布式主要的问题是网络问题,所以我们优先处理分布式的网络问题,然后在一致性和可用性之间权衡。这时提出了 BASE 就是对 CAP 的一致性和可用性的权衡结果。
BASE 也叫基本可用,也就是最终一致性原则
最终一致性:就是你提交一台机器,在一定时间内,数据会不一致,其他机器最终会把你提交的那台机器给同步到自己上,数据会最终一致。
二 分布式事务、2PC、3PC 、Paxos
数据不一致问题:
在分布式架构数据有多副本的情况下,如果网络、服务器或者软件出现故障,会导致部分副本写入成功,部分副本写入失败。这就造成各个副本之间的数据不一致,数据内容冲突,造成事实上的数据不一致。
2PC
两阶段提交协议是协调所有分布式原子事务参与者,并决定提交或取消(回滚)的分布式算法。
-
协议参与者
在两阶段提交协议中,系统一般包含两类机器(或节点):一类为协调者(coordinator),通常一个系统中只有一个;另一类为事务参与者(participants,cohorts 或 workers),一般包含多个,在数据存储系统中可以理解为数据副本的个数。
协议中假设每个节点都会记录写前日志(write-ahead log)并持久性存储,即使节点发生故障日志也不会丢失。协议中同时假设节点不会发生永久性故障而且任意两个节点都可以互相通信。
-
两个阶段的执行
1.请求阶段(commit-request phase,或称表决阶段,voting phase)
在请求阶段,协调者将通知事务参与者准备提交或取消事务,然后进入表决过程。
在表决过程中,参与者将告知协调者自己的决策:同意(事务参与者本地作业执行成功)或取消(本地作业执行故障)。
2.提交阶段(commit phase)
在该阶段,协调者将基于第一个阶段的投票结果进行决策:提交或取消。
当且仅当所有的参与者同意提交事务协调者才通知所有的参与者提交事务,否则协调者将通知所有的参与者取消事务。
参与者在接收到协调者发来的消息后将执行响应的操作。
-
两阶段提交的缺点
1. 同步阻塞问题
执行过程中,所有参与节点都是事务阻塞型的。当参与者占有公共资源时,其他第三方节点访问公共资源不得不处于阻塞状态。
2.单点故障
由于协调者的重要性,一旦协调者发生故障。参与者会一直阻塞下去。尤其在第二阶段,协调者发生故障,那么所有的参与者还都处于锁定事务资源的状态中,而无法继续完成事务操作。(如果是协调者挂掉,可以重新选举一个协调者,但是无法解决因为协调者宕机导致的参与者处于阻塞状态的问题)
3.数据不一致
在二阶段提交的阶段二中,当协调者向参与者发送 commit 请求之后,发生了局部网络异常或者在发送 commit 请求过程中协调者发生了故障,这回导致只有一部分参与者接受到了 commit 请求。
而在这部分参与者接到 commit 请求之后就会执行 commit 操作。但是其他部分未接到 commit 请求的机器则无法执行事务提交。于是整个分布式系统便出现了数据部一致性的现象。
-
两阶段提交无法解决的问题
-
当协调者出错,同时参与者也出错时,两阶段无法保证事务执行的完整性。
-
考虑协调者在发出 commit 消息之后宕机,而唯一接收到这条消息的参与者同时也宕机了。那么即使协调者通过选举协议产生了新的协调者,这条参与者事务的状态也是不确定的,没人知道事务是否被已经提交。
3PC
三阶段提交协议在协调者和参与者中都引入超时机制,并且把两阶段提交协议的第一个阶段拆分成了两步:询问,然后再锁资源,最后真正提交。
-
三个阶段的执行
-
CanCommit 阶段
3PC 的 CanCommit 阶段其实和 2PC 的准备阶段很像。
协调者向参与者发送 commit 请求,参与者如果可以提交就返回 Yes 响应,否则返回 No 响应。
-
PreCommit 阶段
协调者根据参与者的反应情况来决定是否可以继续事务的 PreCommit 操作。
根据响应情况,有以下两种可能:
-
假如协调者从所有的参与者获得的反馈都是 Yes 响应,那么就会进行事务的预执行:
-
发送预提交请求。协调者向参与者发送 PreCommit 请求,并进入Prepared 阶段。
-
事务预提交。参与者接收到 PreCommit 请求后,会执行事务操作,并将 undo 和 redo 信息记录到事务日志中。
-
响应反馈。如果参与者成功的执行了事务操作,则返回 ACK 响应,同时开始等待最终指令。
2.假如有任何一个参与者向协调者发送了 No 响应,或者等待超时之 后,协调者都没有接到参与者的响应,那么就中断事务:
-
发送中断请求。协调者向所有参与者发送中断请求。
-
中断事务。参与者收到来自协调者的中断请求之后(或超时之后,仍未收到中断的请求),执行事务的中断。
-
DoCommit 阶段
该阶段进行真正的事务提交,也可以分为以下两种情况:
-
执行提交
-
发送提交请求。协调者接收到参与者发送的 ACK 响应,那么他将从预提交状态进入到提交状态。并向所有参与者发送 doCommit 请求。
-
事务提交。参与者接收到 doCommit 请求之后,执行正式的事务提交。并在完成事务提交之后释放所有事务资源。
-
响应反馈。事务提交完之后,向者发送 ACK 响应。
-
完成事务。协调者接收到所有参与者的 ACK 响应之后,完成事务。
-
中断事务
协调者没有接收到参与者发送的ACK响应(可能是接受者发送的不是 ACK 响应,也可能响应超时),那么就会执行中断事务。
-
三阶段提交协议和两阶段提交协议的不同
对于协调者(Coordinator)和参与者(Cohort)都设置了超时机制(在 2PC 中,只有协调者拥有超时机制,即如果在一定时间内没有收到参与者的消息则默认失败)。
在 2PC 的准备阶段和提交阶段之间,插入预提交阶段,使 3PC 拥有CanCommit、PreCommit、DoCommit 三个阶段。
PreCommit 是一个缓冲,保证了在最后提交阶段之前各参与节点的状态是一致的。
-
三阶段提交协议的缺点
如果进入 PreCommit 后,协调者发出的是中断请求,假设只有一个参与者收到并进行了中断操作;
而其他对于系统状态未知的参与者会根据 3PC 选择继续 Commit,此时系统状态发生不一致性。
paxos 半数协议
有个叫 paxos小岛,岛上居民每项决定都得通过提议然后半数才能生效,每个决定的提议都有一个唯一的全局编号,这个编号只能自增长,不能后退。
何为通过:就是提议的 id 号要大于议员手记录的最大的 id
第一阶段:提议者发起提议给每个议员,然后等议员反馈同意或不同意。
第二阶段:如果半数以上同意了,则执行事务,否则不执行。
如果半数以上同意了,这个议题就通过,然后提议者就命令剩下的议员同步自己的数据,并修改手上的最大 id 号。
问题:
在分布式中并发是常见的,例如现在有提议者 p1,p2
提议者同时提出一个提议,这个时候他们手上的 id 就有可能是一样,p1的 id 是3,p2的 id 也是3。当 p1 提议给议员(假设议员手上的 id是 2),现在议员先同意了 p1,p2 来访问这个议员,议员告诉他已经同意了议题 id 是 3,p2 的 id 是 3 不同意。然后p2回去加大自己的 id重新请求,议员这时同意了他。p1 收到半数同意准备去通知他们来更新 id 同步数据,可是发现议员们的 id 比自己的大了,然后 p1 又加大 id。这种极端情况,导致死锁了。
这种解决办法就是提议者只有一个,也就是 paxos 里面说的总统。
三 Zookeeper 简介
-
什么是 Zookeeper
Zookeeper 是为了解决分布式一致性问题的工程应用。
Zookeeper 并没有直接用 paxos 协议,而是在 paxos 协议的基础上,提出了符合自己符合实际应用场景的高可用的一致性协议 --- ZAB 原子广播协议。
Zookeeper 分布式一致性的特点:
-
顺序一致性:客户端访问 Zookeeper 的一个节点,发起事务,是排着队到 leader 那让他发起提议,一个一个来;
-
单一视图:任何节点上的数据都是一样的,所以客户端访问任意节点都看到是相同的数据。
-
可靠性:给了一个客户端反馈,同意他的请求,那么就是真的同意了。
-
实时性:Zookeeper 保证在一定时间内,比如 5 秒之后你可以访问到最新数据。这是最终一致性导致的。
-
Zookeeper 设计目标
-
简单的数据模型:就是文件夹的树形结构
-
可以构建集群:
-
顺序访问:客户端提出了一个事务请求,会获得一个唯一的id编号,用于操作的先后顺序;
-
高性能:这里指的是读取数据
-
Zookeeper的几个角色
Zookeeper有几个角色:leader、follower、observer;其中observer 一般不配置,它也不参与投票,observer 可以在不影响写性能的情况下提升集群的读性能;
Zookeeper 中节点有实体机器节点,还有 znode 数据节点。znode 数据节点指的是目录文件夹。数据节点有永久数据节点和临时节点。
-
watcher监听机制
Zookeeper 有 watcher 监听机制,例如一个临时数据节点,如果客户session 中断了,临时节点就删除了,这时 watche r就监听到了。这点就是 hadoop 的 HA 实现机制,zkfc 实现了 Zookeeper 的 watcher机制来自动切换。
-
Zookeeper的权限
Zookeeper 的数据节点就是一个文件夹目录,它有自己的权限机制ACL 。 ACL是Access Control Lists 的简写, ZooKeeper 采用 ACL 策略来进行权限控制,有以下权限:
-
CREATE:创建子节点的权限
-
READ:获取节点数据和子节点列表的权限
-
WRITE:更新节点数据的权限
-
DELETE:删除子节点的权限
-
ADMIN:设置节点 ACL 的权限
实际 Zookeeper 删除、设置、创建目录,这些就是执行权限。
四 ZAB 原子广播协议
-
zab 的三种状态
-
Looking/election : 系统刚启动时或者 Leader 崩溃后正处于选举状态;
-
Following:Follower 节点所处的状态,Follower 与 Leader 处于数据同步阶段;
-
Leading:Leader 所处状态,当前集群中有一个 Leader 为主进程。
-
zab 阶段划分:
宏观上来看分为:
-
崩溃恢复阶段
-
快速选举阶段
-
原子广播阶段
微观上来看分为:
-
leader 选举阶段
节点在选举开始读默认投票给自己,当接收其他节点的选票时,会 根据上面的条件更改自己的选票并重新发送选票给其他节点,当一 个节点的得到票超过半数,该节点会设置自己的状态 leading,其 他节点会设置自己的状态为 following。
成为 leader 的条件:
-
选 epoch 最大的;
-
epoch 相等,选 zxid 最大的
-
epoch和 zxid 都相等,选择 server id最大的(就是配置 zoo.cfg 中的 myid)。
那什么是 epoch,什么是 zxid 呢?
epoch 是 leader 标识,zxid 是事务标识。
epoch 是指:年代,一个领导挂了,另一个领导上任,现在就是新领导的时代了,当产生新领导,事务编号就从0开始。
zxid是总称:前32位是 leader 编号(epoch),后32位是这个 leader下事务编号。
首先 ZooKeeper 一个事务包含两部分,一个是数据,一个是id;
id是全局唯一的 id,数据就是具体操作数据,并且是 lastid 加1,
ZooKeeper 每个请求都是顺序执行的,强顺序性的。
-
发现阶段
发现阶段主要是发现最大的 epoch 和最大的事务编号;
第一阶段快速产生准备 leader,其他节点就是 follower,然后在发现阶段 follower 向 leader报告自己的epoch和事务编号,leader 进行排序,选择最大的 epoch 和最大的事务编号,之后通知 follower 去更改它的 epoch。
-
同步阶段
leader 利用上一个阶段知道最大事务编号,然后通知其他 follower 去leader 这同步数据。事务编号有可能不一样,所以要同步。保持数据最终一致性。
-
原子广播阶段
这时候 leader 真正对外提供服务,接受客户端的请求,生成一个数据,半数以上同意,然后就提交事务。剩下的其他节点直接去 leader 那同步数据。
-
问题:原来挂掉的 leader 的事务怎么处理?
挂掉的 leader 启动起来,发现它的时代已经过时了,就删除事务,发现有新的 leader,自己就变成 follower,然后就去同步数据。
在选举上,会选举拥有最新提议历史( lastzxid最大)的节点作为leader,这样子就省去了发现最新提议的步骤。这是局域拥有最新提议的节点也有最新提交记录的前提。
-
zab 和 paxos 区别与联系
ZAB 协议并不是 Paxos 算法的一个典型实现,在讲解 ZAB 和 Paxos 之间的区别之前,我们首先来看下两者的联系。
-
两者都存在一个类似于Leader进程的角色,由其负责协调多个 Follow 进程的运行。
-
Leader 进程都会等待超过半数的 Follower 做出正确的反馈后,才会将一个提案进行提交。
-
在ZAB协议中,每个提议中都包含了一个 epoch 值,用来代表当前Leader 周期,在 Paxos 算法中,同样存在这样一个标识,只是名字变成了 Ballot。
在 Paxos 算法中,一个新选举产生的主进程会进行读和写两个阶段的工作。
第一阶段被称为读阶段,在这个阶段中,这个新的主进程会通过和所有其他进程进行通信的方式来收集上一个主进程的提案,并将他们提交。
第二阶段被称为写阶段,在这个阶段,当前主进程开始提出他自己的提案。
在Paxos算法设计的基础上,ZAB 协议额外添加了一个同步阶段,ZAB 会进行 发现阶段(类似 paxos 读阶段)、同步阶段、写阶段(类似 paxos 写阶段)。
在同步阶段之前,ZAB 协议也存在一个和 Paxos 算法中的读阶段非常类似的过程,称为发现(Discovery)阶段。
在同步阶段中,新的 Leader 会确保存在过半的 Follower 已经提交了之前 Leader 周期中的所有事务 提议 。
这一同步阶段的引入,能够有效地保证 Leader 在新的周期中提出事务提议之前,所有的进程都已经完成了对之前所有事务提议的提交。
一旦完成同步阶段后,那么 ZAB 就会执行和 Paxos 算法类似的写阶段。
总的来讲,ZAB 协议和 Paxos 算法的本质区别在于,两者的设计目标不太一样。
ZAB 协议主要用于构建一个高可用的分布式数据主备系统,例如ZooKeeper,而 Paxos 算法则是用于构建一个分布式的一致性状态机系统。
五 Zookeeper 应用
ZooKeeper 目录有几个特点,有临时目录,永久目录,顺序目录,强一致性(顺序访问)和 watcher 机制。
利用这些特点,我们可以实现:
-
发布订阅,例如一些配置信息;
-
负载均衡,例如kafka生产,消费均衡;
-
master选举,例如 hbase 利用它 hmaster 选举;
-
主备切换,例如 hdfs 的 HA 利用它进行切换。
-
发布订阅
例如:我们的数据库配置信息文件就可以放到 ZooKeeper 上。
利用 ZooKeeper 的 watcher 机制实现配置变更,程序在运行中就可以获取到最新的配置信息,不需要起停。
2. ZooKeeper HA 应用(主备切换)
hdfs 的 HA,它的主备切换用的是 ZooKeeper 来做 Active standby 之间切换的。
大致步骤:
-
多个 nodemanager 同时向 ZooKeeper 注册一个数据节点 lock 。因为 ZooKeeper 是强一致性的,所以只能有一个注册成功,注册成功的那个就是 active
-
没有注册成功的否成了 standby,然后在 lock 目录上注册监听事件 watcher。
注意:注册的 lock 节点目录是临时节点,如果 active 挂了,这个目录也就没了,并且这个 lock 目录是有权限控制的 ACL,防止 active 假死后重新连接出现脑裂。
-
主备切换:当 active 挂掉以后,会话结束,临时目录自动删除。其他 standby 监听到临时目录删除了,各个 standby 重新同时进行创建带权限的临时目录。成功的改为 active,没有成功的还是standby
-
如果挂掉的 active 启动后,发现没有权限范围lock临时目录,自动更改成 standby 状态。
这是 ZooKeeper 主备切换应用,利用临时目录,ACL,watcher机制实现。
3. master选举
利用 Zookeeper master 选举其实很简单,和主备切换一样。
利用强一致性同时创建同一个目录,最后只能一个成功,成功那个节点会返回成功状态,其他节点返回异常,成功的那个就成为 master,其他节点就改成 slave 。
4. Zookeeper 在 hbase 中应用
hmaster 监控 regionserver 是否挂掉。
首先,rs(regionserver简称)在ZooKeeper注册一个临时目录rs/[hostname] 目录,然后 hmaster 注册 watcher 监控 rs 目录下面变化就可以发现 rs 服务器是否挂掉。
元数据存储(订阅发布):每个 region 存储信息位置和状态,都放在 ZooKeeper 上存储,以便大家都能订阅目前 region 所处的状态,比如 region 是在合并还是在切割,有多少个 region 分别在哪个regionserver 上。
所以客户端访问先访问 Zookeeper 得到位置信息去读取数据,不经过hmaster。
5. Zookeeper 在kafka中应用
kafka 在 Zookeeper 注册的信息
首先,kafka 会把 broker 创建到 Zookeeper 临时目录上。
/broker/ids/[1-n] 表示 broker 还活着。然后 topic 信息创建到 Zookeeper 临时目录/brokers/topics/[topicname]/paritiong 信息。如果有消费者,消费者也会在 Zookeeper 创建自己消费信息的offset信息等临时目录。
kafka 注册 broker 和 topic 信息使为了生产消费时负载均衡,这就利用到 Zookeeper 负载均衡。消费生产者监控到 broker 和 topic ,topic 和 partition 之间的数量,进行重新排序。
参考书籍:
《从Paxos到Zookeeper 分布式一致性原理与实践》
少侠,我看你骨骼精奇,关注一下,我这本武林秘籍就是你的了~