Zookeeper的架构组成

Leader

  • Zookeeper 集群工作的核心角色,集群内部各个服务器的调度者。
  • 事务请求(写操作) 的唯一调度和处理者,保证集群事务处理的顺序性;对于 create,setData, delete 等有写操作的请求,则需要统一转发给leader 处理, leader 需要决定编号、执行操作,这个过程称为一个事务。

    Follower

  • 处理客户端非事务(读操作) 请求,
    转发事务请求给 Leader;
    参与集群 Leader 选举投票 2n-1台可以做集群投票

    Observer

  • 观察者角色,观察 Zookeeper 集群的最新状态变化并将这些状态同步过来,其对于非事务请求可以进行独立处理,对于事务请求,则会转发给 Leader服务器进行处理。
    不会参与任何形式的投票只提供非事务服务,通常用于在不影响集群事务处理能力的前提下提升集群的非事务处理能力。增加了集群增加并发的读请求

image.png

Zookeeper 特点

  1. Zookeeper:一个领导者(leader:老大),多个跟随者(follower:小弟)组成的集群。
    2. Leader负责进行投票的发起和决议,更新系统状态(内部原理)
    3. Follower用于接收客户请求并向客户端返回结果,在选举Leader过程中参与投票
    4. 集群中只要有半数以上节点存活,Zookeeper集群就能正常服务。
    5. 全局数据一致:每个server保存一份相同的数据副本,Client无论连接到哪个server,数据都是一
    致的。
    6. 更新请求顺序进行(内部原理)
    7. 数据更新原子性,一次数据更新要么成功,要么失败。

Zookeeper数据结构与监听机制

Zookeeper 节点数据类型

持久性节点(Persistent)

持久节点:是Zookeeper中最常见的一种节点类型,所谓持久节点,就是指节点被创建后会一直存在服务器,直到删除操作主动清除。
持久顺序节点:就是有顺序的持久节点,节点特性和持久节点是一样的,只是额外特性表现在顺序上。
顺序特性实质是在创建节点的时候,会在节点名后面加上一个数字后缀,来表示其顺序。

临时性节点(Ephemeral)

临时节点:就是会被自动清理掉的节点,它的生命周期和客户端会话绑在一起,客户端会话结束,节点会被删除掉。与持久性节点不同的是,临时节点不能创建子节点。
临时顺序节点:就是有顺序的临时节点,和持久顺序节点相同,在其创建的时候会在名字后面加上数字后缀。

顺序性节点(Sequential)

事务ID

在ZooKeeper中,事务是指能够改变ZooKeeper服务器状态的操作,我们也称之为事务操作或更新操作,一般包括数据节点创建与删除、数据节点内容更新等操作。对于每一个事务请求,ZooKeeper都会为其分配一个全局唯一的事务ID,用 ZXID 来表示,通常是一个 64 位的数字。每一个 ZXID 对应一次更新操作,从这些ZXID中可以间接地识别出ZooKeeper处理这些更新操作请求的全局顺序
zk中的事务指的是对zk服务器状态改变的操作(create,update data,更新字节点);zk对这些事务操作都会编号,这个编号是自增长的被称为ZXID。

Watcher 机制

使用watcher机制实现分布式数据的发布/订阅功能
在 ZooKeeper 中,引入了 Watcher 机制来实现这种分布式的通知功能。ZooKeeper 允许客户端向服务端注册一个 Watcher 监听,当服务端的一些指定事件触发了这个 Watcher,那么Zk就会向指定客户端发送一个事件通知来实现分布式的通知功能。
image.png
Zookeeper的Watcher机制主要包括客户端线程客户端WatcherManagerZookeeper服务器三部分。
具体工作流程为:

  • 客户端在向Zookeeper服务器注册的同时,会将Watcher对象存储在客户端的WatcherManager当中
  • 当Zookeeper服务器触发Watcher事件后,会向客户端发送通知
  • 客户端线程从WatcherManager中取出对应的Watcher对象来执行回调逻辑

Leader选举机制

  • 半数机制:集群中半数以上机器存活,集群可用。所以Zookeeper适合安装奇数台服务器。
  • Zookeeper虽然在配置文件中并没有指定Master和Slave。但是,Zookeeper工作时,是有一个节
    点为Leader,其它为Follower,Leader是通过内部的选举机制产生的。

内部的选举机制

集群首次启动

关键词:顺序启动,ID值,半数胜出

  • id值较大的服务器胜出,后面启动的都是小弟
  • 服务器顺序启动,当一半以上的服务器启动,马上id最大的成为leader
  • 再后面启动的节点,都沦为follower

    集群非首次启动

    每个节点在选举时都会参考自身节点的ZXID值(事务ID);优先选择ZXID值最大的节点称为Leader!!

ZAB一致性协议

Q:为什么会出现分布式数据一致性问题?
在分布式系统中引入数据复制机制后,多台数据节点之间由于网络等原因很容易产生数据不一致的情况。
image.png

1. 主备模式保证一致性

所有数据都由主进程处理,然后复制给副本。
所有客户端写入数据都是写入Leader中,然后,由 Leader 复制到Follower中。ZAB会将服务器数据的状态变更以事务Proposal的形式广播到所有的副本进程上,ZAB协议能够保证了事务操作的一个全局的变更序号(ZXID)
image.png

2. 广播消息

ZAB 协议的消息广播过程类似于 二阶段提交过程。对于客户端发送的写请求,全部由 Leader 接收,
Leader 将请求封装成一个事务 Proposal(提议),将其发送给所有 Follwer ,如果收到超过半数反馈
ACK,则执行 Commit 操作(先提交自己,再发送 Commit 给所有 Follwer)。
image.png
image.png

image.png
不能正常反馈的Follower恢复正常后会进入数据同步阶段最终与Leader保持一致!!
细节

  • Leader接收到Client请求之后,会将这个请求封装成一个事务,并给这个事务分配一个全局递增的
    唯一 ID,称为事务ID(ZXID),ZAB 协议要求保证事务的顺序,因此必须将每一个事务按照 ZXID
    进行先后排序然后处理。
  • ZK集群为了保证任何事务操作能够有序的顺序执行,只能是 Leader 服务器接受写请求,即使是
    Follower 服务器接受到客户端的请求,也会转发到 Leader 服务器进行处理。

3. Leader 崩溃问题

Leader宕机后,被选举的新Leader需要解决的问题

  • ZAB 协议确保那些已经在 Leader 提交的事务最终会被所有服务器提交(commit)。
  • ZAB 协议确保丢弃那些只在 Leader 提出/复制,但没有提交的事务。

这个选举算法的关键点:保证选举出的新Leader拥有集群中所有节点最大编号(ZXID)的事务!!
也就是说,该follow已经接收到了leader崩溃前的最大的commit事物

Zookeeper应用

Zookeeper的两大特性:

  1. 客户端如果对Zookeeper的数据节点注册Watcher监听,那么当该数据节点的内容或是其子节点列表发生变更时,Zookeeper服务器就会向订阅的客户端发送变更通知。
  2. 对在Zookeeper上创建的临时节点,一旦客户端与服务器之间的会话失效,那么临时节点也会被 自动删除

一、服务器动态上下线监听

分布式系统中,主节点(Servers)会有多台,主节点可能因为任何原因出现宕机或者下线,而任意一台客户端(Client)都要 能实时感知到主节点服务器的上下线。
实现:监听servers目录下的节点是否变化(临时带顺序的节点)
servers/server1
servers/server2
servers/server3….
image.png

二、分布式锁 【重要】

  • 单机程序中,当存在多个线程可以同时改变某个变量(可变共享变量)时,为了保证线程安全(数据不能出现脏数据)就需要对变量或代码块做同步,使其在修改这种变量时能够串行执行消除并发修改变量。
  • 对变量或者堆代码码块做同步本质上就是加锁。目的就是实现多个线程在一个时刻同一个代码块只能有一个线程可执行

    分布式锁

    image.png
    容易出现的问题:
    在第一个订单A执行到第3步时(库存1-1=0),另一个订单B执行第2步,发现库存还是1,实际上已经是0
    解决方法:
    用锁把 2、3、4 步锁住,让他们执行完之后,另一个线程才能进来执行。
    image.png
    但是,当两个订单在两台机器上执行,系统是运行在两个不同的 JVM 里面,不同的机器上,增加的锁只对自己当前 JVM 里面的线程有效,对于其他 JVM 的线程是无效的。
    需要保证两台机器加的锁是同一个锁,此时分布式锁就能解决该问题。

    zk实现分布式锁思路

    利用Zookeeper可以创建临时带序号节点的特性来实现一个分布式锁
    步骤:
  1. 锁就是zk指定目录下序号最小的临时序列节点,多个系统的多个线程都要在此目录下创建临时的顺序节点,因为Zk会为我们保证节点的顺序性,所以可以利用节点的顺序进行锁的判断。
  2. 每个线程都是先创建临时顺序节点,然后获取当前目录下最小的节点(序号),判断最小节点是不是当前节点,如果是那么获取锁成功,如果不是那么获取锁失败。
  3. 获取锁失败的线程获取当前节点上一个临时顺序节点,并对对此节点进行监听,当该节点删除的时候(上一个线程执行结束删除或者是掉线zk删除临时节点)这个线程会获取到通知,代表获取到了锁

image.png

Hadoop HA

NameNode单点故障SPOF

NameNode机器发生意外,如宕机,集群将无法使用,直到管理员重启
NameNode机器需要升级,包括软件、硬件升级,此时集群也将无法使用
解决:
HDFS HA功能通过配置Active/Standby两个NameNodes实现在集群中对NameNode的热备来解决上述问题。如果出现故障,如机器崩溃或机器需要升级维护,这时可通过此种方式将NameNode很快的切换到另外一台机器

HDFS-HA 工作机制

通过双NameNode消除单点故障(Active/Standby)

HDFS-HA工作要点

  1. 元数据管理方式需要改变
    内存中各自保存一份元数据;
    Edits日志只有Active状态的NameNode节点可以做写操作;
    两个NameNode都可以读取Edits;
    共享的Edits放在一个共享存储中管理(qjournal和NFS两个主流实现);
    2. 需要一个状态管理功能模块
    实现了一个zkfailover,常驻在每一个namenode所在的节点,每一个zkfailover负责监控自己所在NameNode节 点,利用zk进行状态标识,当需要进行状态切换时,由zkfailover来负责切换,切换时需要防止brain split现象的发生(集群中出现两个Active的Namenode)。
    3. 必须保证两个NameNode之间能够ssh无密码登录
    4. 隔离(Fence),即同一时刻仅仅有一个NameNode对外提供服务

HDFS-HA工作机制

配置部署HDFS-HA进行自动故障转移。自动故障转移为HDFS部署增加了两个新组件:ZooKeeper和ZKFailoverController(ZKFC)进程,ZooKeeper是维护少量协调数据,通知客户端这些数据的改变和监视客户端故障的高可用服务。HA的自动故障转移依赖于ZooKeeper的以下功能:

  • 故障检测
    • 集群中的每个NameNode在ZooKeeper中维护了一个临时会话,如果机器崩溃,ZooKeeper中的会话将终止,ZooKeeper通知另一个NameNode需要触发故障转移
  • 现役NameNode选择
    • ZooKeeper提供了一个简单的机制用于唯一的选择一个节点为active状态。如果目前现役NameNode崩溃,另一个节点可能从ZooKeeper获得特殊的排外锁以表明它应该成为现役NameNode。

ZKFC是自动故障转移中的另一个新组件,是ZooKeeper的客户端,也监视和管理NameNode的状态。每个运行NameNode的主机也运行了一个ZKFC进程,ZKFC负责:

  • 健康监测
    • ZKFC使用一个健康检查命令定期地ping与之在相同主机的NameNode,只要该NameNode及时地回复健康状态,ZKFC认为该节点是健康的。如果该节点崩溃,冻结或进入不健康状态,健康监测器标识该节点为非健康的。
  • ZooKeeper会话管理
    • 当本地NameNode是健康的,ZKFC保持一个在ZooKeeper中打开的会话。如果本地NameNode处于active状态,ZKFC也保持一个特殊的znode锁,该锁使用了ZooKeeper对短暂节点的支持,如果会话终止,锁节点将自动删除
  • 基于ZooKeeper的选择
    • 如果本地NameNode是健康的,且ZKFC发现没有其它的节点当前持有znode锁,它将为自己获取该锁。如果成功,则它已经赢得了选择,并负责运行故障转移进程以使它的本地NameNode为Active。故障转移进程与前面描述的手动故障转移相似,首先如果必要保护之前的现役NameNode,然后本地NameNode转换为Active状态。image.png

      YARN-HA工作机制

      略。。。