美文网首页
Zookeeper学习笔记

Zookeeper学习笔记

作者: 三十四桥明月夜 | 来源:发表于2020-03-27 20:54 被阅读0次

    简介

    ZooKeeper 由雅虎研究院开发,后来捐赠给了 Apache。ZooKeeper 是一个开源的分布式应用程序协调服务器,其为分布式系统提供一致性服务。其一致性是通过基于 Paxos 算法的 ZAB 协议完成的。

    其主要功能包括:

    • 配置维护

    • 域名服务

    • 分布式同步

    • 集群管理等

    ZOOKEEPER理论基础

    一、一致性

    • 顺序一致性

    • 原子性

    • 单一视图

    • 最终一致性

    二、Paxos算法

    前提:不存在拜占庭将军问题,也就是说信道是通畅的。

    算法三种角色

    在 Paxos 算法中有三种角色,分别具有三种不同的行为。但很多时候,一个进程可能同 时充当着多种角色。

    • Proposer:提案者,提议者,proposal的提议者

    • Acceptor:表决者

    • Learners:学习者

    算法的两个阶段

    • prepare阶段

    • accept阶段

    通俗来讲,prepare阶段是提案者向系统中的其他表决者发送一个提案,其目的是通过一个全局唯一且自增长的N值来确定在表决者那里这个提案是否是最新的,如果不是最新的,表决者是不会接收这个提案的,这种情况下,提案者可以选择重新提案或者放弃提案。

    当提案者发现超过半数的表决者都已经接收了这个提案,将进入accept阶段。提案者会给每个表决者发送他的提案,这个阶段依然还要用与prepare阶段相同的策略来确定表决者那里这个提案还是不是最新的,不是最新的话,表决者将拒绝这个提案。若最终提案者发现超过半数的的表决者通过了这个提案,提案者会向外发送两类信息:

    • 向曾 accept 其提案的表决者发送“可执行数据同步信号”,即让它们执行其曾接收到的提案;

    • 向未曾向其发送 accept 反馈的表决者发送“提案 + 可执行数据同步信号”,即让

      它们接受到该提案后马上执行。

    可以看到,paxos算法通过这种策略,来保证所有角色的一致性。

    这里有一个问题:

    在一个提案者提交提案的时候,其他提案者也是表决者从而参与表决吗?

    活锁问题

    死锁

    死锁很好理解

    截屏2020-03-17下午9.34.03.png

    活锁

    截屏2020-03-17下午9.37.20.png

    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没有的呢?

    初始化广播算法图解

    截屏2020-03-20下午5.52.56.png

    问题:

    这里leader为每一个learner都创建一个队列,这个没明白什么意思,一个队列不行吗?一个队列就能够保证顺序了啊。难道是因为每个learner中的所保存的事务请求不同,leader为了针对每个learner的这种差异性去保存不同的事务请求,才设计了多个队列,恩,极有可能是这个原因。

    2. 更新广播算法

    当learner收到事务请求要把这个请求转发给leader去处理,然后算过更新广播算法把这个事务请求更新到每一个learner当中。算法图解如下:

    截屏2020-03-20下午6.10.38.png

    这里有两个问题

    1. fast paxos算法,是只有一个角色可以发请求,表决时,发送到follwer处的请求的zxid一定是比follwer的zxid大的。为什么还要进行第三步的比较?follower直接接受提案进行同步就好了啊。
    答:ABA问题。
    
    1. 第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之间的心跳来进行维护(上边已经说了是怎么进行维护的)

    恢复模式的三个原则

    首先,说说进入恢复模式的三种场景。

    1. 集群启动,leader与其他learner的连接数没有过半

    2. leader宕机,并且已经向其他learner同步了一些事务请求

    3. leader宕机,没有向其他learner同步事务请求,然后leader又起来了

    以上三种场景,每种场景进入恢复模式的时候都会对应一个原则

    1. Leader主动出让原则

      第一种场景下,leader你都跟其他learner连不上了,你得主动出让自己的learner权利。如果任何一个learner发现自己与leader连不上了就会进入LOOKING状态,当有过半的learner处于LOOKING状态,将会重新选举leader。

    2. 已处理过的消息不能丢失原则

      第二种场景下,会在已经同步完数据的follower中进行leader的选举,选举完成后,新的leader会用与更新广播算法相似的形式来向其他没有完成同步的follower进行数据同步。

      同步过程:

      follower会先向下递归,找到xid与新leader的xid相同并且内容也相同的项,然后再向上递归进行数据同步,知道同步到最大的xid。

    3. 被丢弃的事务不能再现

      第三种场景下,leader此时已经不再是leader,它只要作为一个follower向新的leader按照之前所说过的同步算法进行数据同步就好了。

    leader选举(重点,还要阅读源码)

    截屏2020-03-20下午8.13.58.png 截屏2020-03-20下午8.17.03.png

    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

    截屏2020-03-26下午2.00.22.png

    结点类型

    • 持久节点

    • 持久顺序节点

    • 临时节点

    • 临时顺序节点

    结点状态

    • 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 机制实现了发布/订阅模式

    截屏2020-03-26下午2.16.30.png

    相关文章

      网友评论

          本文标题:Zookeeper学习笔记

          本文链接:https://www.haomeiwen.com/subject/bniruhtx.html