Zookeeper架构一之原理

zookeeper是什么

Zookeeper 分布式服务框架是Apache Hadoop 的一个子项目,它主要是用来解决分布式应用中经常遇到的一些数据管理问题,如:统一命名服务、状态同步服务、集群管理、分布式应用配置项的管理等。

zookeeper提供了什么

简单的说,zookeeper=文件系统+通知机制。

1. 文件系统
Zookeeper维护一个类似文件系统的数据结构:

每个子目录项如 NameService 都被称作为 znode,znode是一个跟Unix文件系统路径相似的节点,可以往这个节点存储或获取数据。如果在创建znode时Flag设置为EPHEMERAL,那么当创建这个znode的节点和Zookeeper失去连接后,这个znode将不再存在在Zookeeper里,Zookeeper使用Watcher察觉事件信息。当客户端接收到事件信息,比如连接超时、节点数据改变、子节点改变,可以调用相应的行为来处理数据。

  • stat
    ZooKeeper数据模型中的每个znode都维护着一个 stat 结构。 stat仅提供znode的元数据。 它由版本号,操作控制列表(ACL),时间戳和数据长度组成。如:
{
    "aversion":0,
    "czxid":154618822678,
    "dataLength":11,
    "numChildren":3,
    "ctime":1609250712310,
    "ephemeralOwner":0,
    "pzxid":154618822682,
    "cversion":3,
    "mtime":1609250712310,
    "version":0,
    "mzxid":154618822678
}
  • 版本号
    每个znode都有版本号,这意味着每次与znode关联的数据发生更改时,其对应的版本号也会增加。 当多个zookeeper客户端尝试在同一znode上执行操作时,版本号的使用很重要。
  • 操作控制列表(ACL)
    ACL基本上是访问znode的认证机制。 它管理所有znode读取和写入操作。
  • 时间戳
    时间戳表示创建和修改znode所经过的时间。 它通常表示为毫秒。 ZooKeeper标识“事务ID"(zxid)对znode的每个更改。 Zxid 是唯一的,并且为每个事务维护时间,以便您可以轻松地确定从一个请求到另一个请求的时间。
  • 数据长度
    存储在znode中的数据总量是数据长度。 您最多可以存储1MB的数据。
  • znode有四种类型
  1. PERSISTENT-持久化目录节点
    客户端与zookeeper断开连接后,该节点依旧存在。默认情况下,除非另有说明,否则所有znode都是持久的。
CountDownLatch latch = new CountDownLatch(1);
        ZooKeeper zooKeeper = new ZooKeeper("127.0.0.1:2181,127.0.0.1:2182,127.0.0.1:2183", 5000, new Watcher() {
            @Override
            public void process(WatchedEvent watchedEvent) {
                System.out.println(watchedEvent);
                if (watchedEvent.getState() == Event.KeeperState.SyncConnected) {
                    latch.countDown();
                }
            }
        });

        latch.await();

        String path1 = zooKeeper.create("/yangyanping", "".getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);
        System.out.println(path1);
  1. PERSISTENT_SEQUENTIAL-持久化顺序编号目录节点
    客户端与zookeeper断开连接后,该节点依旧存在,只是Zookeeper给该节点名称进行顺序编号,在节点yangyanping下创建3个顺序节点yyp。

  2. EPHEMERAL-临时目录节点
    临时znode有效,直到客户端活着。 当客户端从ZooKeeper集合断开连接时,临时znode会自动删除。 为此,只有临时znode不允许有子节点。 如果删除临时znode,则下一个合适的节点将填充其位置。 临时znode在领导选举中发挥重要作用。

  3. EPHEMERAL_SEQUENTIAL-临时顺序编号目录节点
    顺序znode可以是持久的或短暂的。 当一个新的znode被创建为一个连续的znode,然后ZooKeeper设置znode的路径,通过附加一个10位的序列号到原始名称。 例如,如果将具有路径 /myapp 的znode创建为顺序znode,则ZooKeeper将路径更改为 /myapp0000000001 ,并将下一个序列号设置为/myapp0000000002.如果两个 顺序znode是并发创建的,那么ZooKeeper从不对每个znode使用相同的数字。 顺序znode在锁定和同步中起重要作用。

2. 通知机制

客户端注册监听它关心的目录节点,当目录节点发生变化(数据改变、被删除、子目录节点增加删除)时,zookeeper会通知客户端。

zookeeper可以做什么

  • 命名服务
    在zookeeper的文件系统里创建一个目录,即有唯一的path。在我们使用tborg无法确定上游程序的部署机器时即可与下游程序约定好path,通过path即能互相探索发现,不见不散了

  • 配置管理
    程序总是需要配置的,如果程序分散部署在多台机器上,要逐个改变配置就变得困难。好吧,现在把这些配置全部放到zookeeper上去,保存在 Zookeeper 的某个目录节点中,然后所有相关应用程序对这个目录节点进行监听,一旦配置信息发生变化,每个应用程序就会收到 Zookeeper 的通知,然后从 Zookeeper 获取新的配置信息应用到系统中就好。

  • 集群管理
    所谓集群管理无在乎两点:是否有机器退出和加入、选举master。
    对于第一点,所有机器约定在父目录GroupMembers下创建临时目录节点,然后监听父目录节点的子节点变化消息。一旦有机器挂掉,该机器与 zookeeper的连接断开,其所创建的临时目录节点被删除,所有其他机器都收到通知:某个兄弟目录被删除。新机器加入 也是类似,所有机器收到通知:新兄弟目录加入。
    对于第二点,我们稍微改变一下,所有机器创建临时顺序编号目录节点,每次选取编号最小的机器作为master就好。

  • 分布式锁
    有了zookeeper的一致性文件系统,锁的问题变得容易。锁服务可以分为两类,一个是保持独占,另一个是控制时序。
    对于第一类,我们将zookeeper上的一个znode看作是一把锁,通过createznode的方式来实现。所有客户端都去创建 /distribute_lock 节点,最终成功创建的那个客户端也即拥有了这把锁。
    对于第二类, /distribute_lock 已经预先存在,所有客户端在它下面创建临时顺序编号目录节点,和选master一样,编号最小的获得锁,用完删除,依次方便。

  • 队列管理
    两种类型的队列:
    1、 同步队列,当一个队列的成员都聚齐时,这个队列才可用,否则一直等待所有成员到达。类似java 中的 Barrier。
    2、队列按照 FIFO 方式进行入队和出队操作。
    第一类,在约定目录下创建临时目录节点,监听节点数目是否是我们要求的数目。
    第二类,和分布式锁服务中的控制时序场景基本原理一致,入列有编号,出列按编号。

    终于了解完我们能用zookeeper做什么了,可是作为一个程序员,我们总是想狂热了解zookeeper是如何做到这一点的,单点维护一个文件系统没有什么难度,可是如果是一个集群维护一个文件系统保持数据的一致性就非常困难了

- 分布式与数据复制

Zookeeper作为一个集群提供一致的数据服务,自然,它要在所有机器间做数据复制。数据复制的好处:

1、 容错
一个节点出错,不致于让整个系统停止工作,别的节点可以接管它的工作;

2、提高系统的扩展能力
把负载分布到多个节点上,或者增加节点来提高系统的负载能力;

3、提高性能
让客户端本地访问就近的节点,提高用户访问速度。

从客户端读写访问的透明度来看,数据复制集群系统分下面两种:

1、写主(WriteMaster)
对数据的修改提交给指定的节点。读无此限制,可以读取任何一个节点。这种情况下客户端需要对读与写进行区别,俗称读写分离;

2、写任意(Write Any)
对数据的修改可提交给任意的节点,跟读一样。这种情况下,客户端对集群节点的角色与变化透明。
对zookeeper来说,它采用的方式是写任意。通过增加机器,它的读吞吐能力和响应能力扩展性非常好,而写,随着机器的增多吞吐能力肯定下降(这 也是它建立observer的原因),而响应能力则取决于具体实现方式,是延迟复制保持最终一致性,还是立即复制快速响应。

我们关注的重点还是在如何保证数据在集群所有机器的一致性,这就涉及到paxos算法。

数据一致性与paxos算法
据说Paxos算法的难理解与算法的知名度一样令人敬仰,所以我们先看如何保持数据的一致性,这里有个原则就是:

在一个分布式数据库系统中,如果各节点的初始状态一致,每个节点都执行相同的操作序列,那么他们最后能得到一个一致的状态。
Paxos算法解决的什么问题呢,解决的就是保证每个节点执行相同的操作序列。好吧,这还不简单,master维护一个全局写队列,所有写操作都必须 放入这个队列编号,那么无论我们写多少个节点,只要写操作是按编号来的,就能保证一致性。没错,就是这样,可是如果master挂了呢。
Paxos算法通过投票来对写操作进行全局编号,同一时刻,只有一个写操作被批准,同时并发的写操作要去争取选票,只有获得过半数选票的写操作才会被 批准(所以永远只会有一个写操作得到批准),其他的写操作竞争失败只好再发起一轮投票,就这样,在日复一日年复一年的投票中,所有写操作都被严格编号排 序。编号严格递增,当一个节点接受了一个编号为100的写操作,之后又接受到编号为99的写操作(因为网络延迟等很多不可预见原因),它马上能意识到自己 数据不一致了,自动停止对外服务并重启同步过程。任何一个节点挂掉都不会影响整个集群的数据一致性(总2n+1台,除非挂掉大于n台)。

总结
Zookeeper 作为 Hadoop 项目中的一个子项目,是 Hadoop 集群管理的一个必不可少的模块,它主要用来控制集群中的数据,如它管理 Hadoop 集群中的 NameNode,还有 Hbase 中 Master Election、Server 之间状态同步等。

Zookeeper的基本概念

  • 角色
  1. Client客户端
    我们的分布式应用程序集群中的一个节点,从服务器访问信息。 对于特定的时间间隔,每个客户端向服务器发送消息以使服务器知道客户端是活着的。类似地,当客户端连接时,服务器发送确认。 如果连接的服务器没有响应,客户端会自动将消息重定向到另一个服务器。
  2. Server
    服务器,我们的ZooKeeper集合中的一个节点,为客户端提供所有的服务。 向客户端发送确认,通知服务器处于活动状态。
  3. Server Cluster
    ZooKeeper服务器组。 形成整体所需的最小节点数为3
  4. Leader领导者
    服务器节点,如果任何连接的节点发生故障,则执行自动恢复。 领导者在服务启动时被选举。领导者负责进行投票的发起和决议,更新系统状态。
  5. Follower跟随者
    Follower 用于接收客户端请求并向客户端返回结果,在选主过程中参与投票
  6. ObServer观察者
    ObServer可以接收客户端连接,将写请求转发给Leader节点,但ObServer不参加投票过程,只同步Leader的状态,ObServer的目的是为了扩展系统,提高读取速度
  • 概念
  1. 会话
    会话对于ZooKeeper的操作非常重要。 会话中的请求按FIFO顺序执行。 一旦客户端连接到服务器,将建立会话并向客户端分配会话ID 。
    客户端以特定的时间间隔发送心跳以保持会话有效。 如果ZooKeeper集合没有从客户端接收到超过在服务启动时指定的时间段(会话超时)的心跳,则它决定客户端死亡。
    会话超时通常用毫秒表示。 当会话由于任何原因结束时,在该会话期间创建的临时znode也会被删除。

  2. 观察者
    观察者(Watcher)是一种简单的机制,使客户端得到关于ZooKeeper集合中的更改的通知。 客户端可以在读取特定znode时设置Watcher。 Watcher会向注册的客户端发送任何znode(客户端注册表)更改的通知。
    Znode更改是与znode的相关数据的修改或znode的孩子的更改。 只触发一次watcher。 如果客户端想要再次通知,则必须通过另一个读取操作来完成。 当连接会话过期时,客户端将与服务器断开连接,相关联的Watcher也将被删除。

ZooKeeper的工作原理

Zookeeper的核心是原子广播,这个机制保证了各个Server之间的同步。实现这个机制的协议叫做Zab协议。Zab协议有两种模式,它们分 别是恢复模式(选主)和广播模式(同步)。当服务启动或者在领导者崩溃后,Zab就进入了恢复模式,当领导者被选举出来,且大多数Server完成了和 leader的状态同步以后,恢复模式就结束了。状态同步保证了leader和Server具有相同的系统状态。
为了保证事务的顺序一致性,zookeeper采用了递增的事务id号(zxid)来标识事务。所有的提议(proposal)都在被提出的时候加上 了zxid。实现中zxid是一个64位的数字,它高32位是epoch用来标识leader关系是否改变,每次一个leader被选出来,它都会有一个 新的epoch,标识当前属于那个leader的统治时期。低32位用于递增计数。

每个Server在工作过程中有四种状态:

  • LOOKING
    竞选状态,当前Server不知道leader是谁,正在搜寻。
  • LEADING
  • 领导者状态,表明当前服务器角色是leader。
  • FOLLOWING
    随从状态,表明当前服务器角色是follower,同步leader状态,参与投票。
  • OBSERVING
    观察状态,表明当前服务器角色是observer,同步leader状态,不参与投票。
    在这里插入图片描述
  1. Zookeeper集群包含1个Leader,多个Follower
  2. 所有的Follower都可提供读服务
  3. 所有的写操作都会被forward到Leader
  4. Client与Server通过NIO通信
  5. 全局串行化所有的写操作
  6. 保证同一客户端的指令被FIFO执行
  7. 保证消息通知的FIFO
  8. 最终一致性:client不论连接到哪个Server,展示给它都是同一个视图,这是zookeeper最重要的性能。
  9. 原子性:更新只能成功或者失败,没有中间状态。
  10. 实时性:Zookeeper保证客户端将在一个时间间隔范围内获得服务器的更新信息,或者服务器失效的信息。但由于网络延时等原因,Zookeeper不能保证两个客户端能同时得到刚更新的数据,如果需要最新数据,应该在读数据之前调用sync()接口。
  11. 顺序性:包括全局有序和偏序两种:全局有序是指如果在一台服务器上消息a在消息b前发布,则在所有Server上消息a都将在消息b前被发布;偏序是指如果一个消息b在消息a后被同一个发送者发布,a必将排在b前面
  12. 可靠性:具有简单、健壮、良好的性能,如果消息m被到一台服务器接受,那么它将被所有的服务器接受。

Zab协议

Zookeeper的核心是原子广播,这个机制保证了各个Server之间的同步。实现这个机制的协议叫做Zab协议。Zab协议有两种模式,它们分别是恢复模式(选主)和广播模式(同步)。当服务启动或者在领导者崩溃后,Zab就进入了恢复模式,当领导者被选举出来,且大多数Server完成了和leader的状态同步以后,恢复模式就结束了。状态同步保证了leader和Server具有相同的系统状态。leader选举是保证分布式数据一致性的关键。

恢复模式

  • 选主流程
    当leader崩溃或者leader失去大多数的follower,这时候zk进入恢复模式,恢复模式需要重新选举出一个新的leader,让所有的 Server都恢复到一个正确的状态。Zk的选举算法有两种:一种是基于basic paxos实现的,另外一种是基于fast paxos算法实现的。系统默认的选举算法为fast paxos。

当zk集群中的一台服务器出现以下两种情况之一时,就会开始leader选举。
(1)服务器初始化启动。
(2)服务器运行期间无法和leader保持连接。
而当一台机器进入leader选举流程时,当前集群也可能处于以下两种状态。
(1)集群中本来就已经存在一个leader。
(2)集群中确实不存在leader。
首先第一种情况,通常是集群中某一台机器启动比较晚,在它启动之前,集群已经正常工作,即已经存在一台leader服务器。当该机器试图去选举leader时,会被告知当前服务器的leader信息,它仅仅需要和leader机器建立连接,并进行状态同步即可。
下面重点看第二种情况,即集群中leader不存在的情况下如何进行leader选举。

  • 数据模型
    投票信息中包含两个最基本的信息。
    sid:即server id,用来标识该机器在集群中的机器序号。
    zxid:即zookeeper事务id号。ZooKeeper状态的每一次改变, 都对应着一个递增的Transaction id, 该id称为zxid. 由于zxid的递增性质, 如果zxid1小于zxid2, 那么zxid1肯定先于zxid2发生. 创建任意节点, 或者更新任意节点的数据, 或者删除任意节点, 都会导致Zookeeper状态发生改变, 从而导致zxid的值增加.
    以(sid,zxid)的形式来标识一次投票信息。例如,如果当前服务器要推举sid为1,zxid为8的服务器成为leader,那么投票信息可以表示为(1,8)

  • 规则
    集群中的每台机器发出自己的投票后,也会接受来自集群中其他机器的投票。每台机器都会根据一定的规则,来处理收到的其他机器的投票,以此来决定是否需要变更自己的投票。
    规则如下:
    (1)初始阶段,都会给自己投票。
    (2)当接收到来自其他服务器的投票时,都需要将别人的投票和自己的投票进行pk,规则如下:
    优先检查zxid。zxid比较大的服务器优先作为leader。
    如果zxid相同的话,就比较sid,sid比较大的服务器作为leader。

  • 举例
    假设当前集群中有5台机器组成。sid分别为1,2,3,4,5。zxid分别为9,9,9,8,8,并且此时sid为2的机器是leader。某一时刻,1和2的服务器挂掉了,集群开始进行选主。
    在第一次投票中,由于无法检测到集群中其他机器的状态信息,因此每台机器都将自己作为被推举的对象来进行投票。于是sid为3,4,5的机器,投票情况分别为(3,9),(4,8),(5,8)
    每台机器把投票发出后,同时也会接收到来自另外两台机器的投票。
    对于server3来说,接收到(4,8),(5,8)的投票,对比后由于自己的zxid要大于收到的另外两个投票,因此不需要做任何变更。
    对于server4来说,接收到(3,9),(5,8)的投票,对比后由于(3,9)这个投票的zxid大于自己,因此需要变更投票为(3,9),然后继续将这个投票发送给另外两台机器。
    对于server5来说,接收到(3,9),(4,8)的投票,对比后由于(3,9)这个投票的zxid大于自己,因此需要变更投票为(3,9),然后继续将这个投票发送给另外两台机器。
    经过第二轮投票后,集群中的每台机器都会再次受到其他机器的投票,然后开始统计投票。判断是否有过半的机器收到相同的投票信息,如果有,那么该投票的sid会成为新的leader。
    机器总数为5台,server3,4,5都收到投票(3,9)。因此server3成为leader。

同步模式

Zookeeper 使用单一的主进程 Leader 来接收和处理客户端所有事务请求,并采用 ZAB 协议的原子广播协议,将事务请求以 Proposal 提议广播到所有 Follower 节点,当集群中有过半的Follower 服务器进行正确的 ACK 反馈,那么Leader就会再次向所有的 Follower 服务器发送commit 消息,将此次提案进行提交。这个过程可以简称为 2pc 事务提交,整个流程可以参考下图,注意 Observer 节点只负责同步 Leader 数据,不参与 2PC 数据同步过程。

  • client发写请求给Follower
  1. Client发送写请求给Zookeeper 集群。
  2. Follower节点收到写请求,会把写请求转发给Leader节点
  3. Leader将所有更新(称为proposal),顺序发送给Follower。
  4. Follower收到Proposal后即将该Proposal写入磁盘,写入成功即返回ACK给Leader
  5. 当Leader收到半数以上的Follower对此proposal的ACK时,即向所有Follower发送
    commit消息,并在本地commit该消息
  6. 每个Proposal都有一个唯一的单调递增的proposal ID,即zxid
  • client发写请求给Leader
  1. Client发送写请求给Zookeeper 集群。
  2. Leader节点收到写请求,将所有更新(称为proposal),顺序发送给Follower
  3. Follower收到Proposal后即将该Proposal写入磁盘,写入成功即返回ACK给Leader
  4. 当Leader收到半数以上的Follower对此proposal的ACK时,即向所有Follower发送
    commit消息,并在本地commit该消息
  5. 每个Proposal都有一个唯一的单调递增的proposal ID,即zxid
  • 同步流程

选完leader以后,zk就进入状态同步过程。

  1. leader等待server连接;
  2. Follower连接leader,将最大的zxid发送给leader;
  3. Leader根据follower的zxid确定同步点;
  4. 完成同步后通知follower 已经成为uptodate状态;
  5. Follower收到uptodate消息后,又可以重新接受client的请求进行服务了。
  • Leader工作流程

Leader主要有三个功能:

  1. 恢复数据;
  2. 维持与Learner的心跳,接收Learner请求并判断Learner的请求消息类型;
  3. Learner的消息类型主要有PING消息、REQUEST消息、ACK消息、REVALIDATE消息,根据不同的消息类型,进行不同的处理。PING消息是指Learner的心跳信息;REQUEST消息是Follower发送的提议信息,包括写请求及同步请求;ACK消息是 Follower的对提议的回复,超过半数的Follower通过,则commit该提议;REVALIDATE消息是用来延长SESSION有效时间。
    Leader的工作流程简图如下所示,在实际实现中,流程要比下图复杂得多,启动了三个线程来实现功能。
  • Follower工作流程
    Follower主要有四个功能:
  1. 向Leader发送请求(PING消息、REQUEST消息、ACK消息、REVALIDATE消息);
  2. 接收Leader消息并进行处理;
  3. 接收Client的请求,如果为写请求,发送给Leader进行投票;
  4. 返回Client结果。

Follower的消息循环处理如下几种来自Leader的消息:

  1. PING消息: 心跳消息;
  2. PROPOSAL消息:Leader发起的提案,要求Follower投票;
  3. COMMIT消息:服务器端最新一次提案的信息;
  4. UPTODATE消息:表明同步完成;
  5. REVALIDATE消息:根据Leader的REVALIDATE结果,关闭待revalidate的session还是允许其接受消息;
  6. SYNC消息:返回SYNC结果到客户端,这个消息最初由客户端发起,用来强制得到最新的更新。

在实际实现中,Follower是通过5个线程来实现功能的。对于observer的流程不再叙述,observer流程和Follower的唯一不同的地方就是observer不会参加leader发起的投票。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值