05.Zookeeper集群

官方文档地址

Zookeeper介绍

Zookeeper是一个分布式的,开放源码的分布式应用程序协调服务,是Google的Chubby一个开源的实现,是Hadoop和Hbase的重要组件。它是一个为分布式应用提供一致性服务的软件,提供的功能包括:配置维护、域名服务、分布式同步、组服务等。

Zookeeper具有高性能高可用性严格排序的访问的特性。Zookeeper的高性能意味着它可以在大型的分布式系统中使用可靠性方面使它不会产生单点故障严格的排序意味着可以在客户端上实现复杂的同步原语

详细测试对比可看官方文档 : 官方文档地址

image-20210228144326215

image-20210228143924298

Zookeeper顺序一致性的理解

Zookeeper的读写机制

  • Zookeeper是一个由多个server组成的集群
  • 一个leader,多个follower
  • 每个server保存一份数据副本
  • 全局数据一致
  • 分布式读写
  • 更新请求转发,由leader实施

Zookeeper 的保证

  • 更新请求顺序进行,来自同一个client的更新请求按其发送顺序依次执行
  • 数据更新原子性,一次数据更新要么成功,要么失败
  • 全局唯一数据视图,client无论连接到哪个server,数据视图都是一致的
  • 实时性,在一定事件范围内,client能读到最新数据

集群中的角色

  • 领导者(leader),负责进行投票的发起和决议,更新系统状态
  • 学习者(learner),包括跟随者(follower)和观察者(observer),follower用于接受客户端请求并想客户端返回结果,在选主过程中参与投票
  • Observer可以接受客户端连接,将写请求转发给leader,但observer不参加投票过程,只同步leader的状态,observer的目的是为了扩展系统,提高读取速度,放大查询能力
  • 客户端(client),请求发起方

183233-20160316222444771-1363762533

节点间连接模式

  •   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:当前Server即为选举出来的leader
  • FOLLOWING:leader已经选举出来,当前Server与之同步

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结果到客户端,这个消息最初由客户端发起,用来强制得到最新的更新。

其他文档:http://www.cnblogs.com/lpshou/archive/2013/06/14/3136738.html

Zookeeper中节点的状态信息各字段含义

image-20210302100241394

@InterfaceAudience.Public
public class Stat implements Record {
  private long czxid; // 该数据节点被创建时的事务id
  private long mzxid; // 该数据节点被修改时最新的事务id(集群中会不一样)
  private long ctime; // 该数据节点创建时间
  private long mtime; // 该数据节点最后修改时间
  private int version; // 当前节点版本号(可以理解为修改次数,每修改一次值+1)
  private int cversion;// 子节点版本号(子节点修改次数,每修改一次值+1)
  private int aversion; // 当前节点acl版本号(acl节点被修改次数,每修改一次值+1)
  private long ephemeralOwner; // 临时节点标示,当前节点如果是临时节点,则存储的创建者的会话id(sessionId),如果不是,那么值=0
  private int dataLength;// 当前节点数据长度
  private int numChildren; // 当前节点子节点个数
  private long pzxid; // 当前节点的父级节点事务ID
  public Stat() {
  }
}

其中zxid表示的是zookeeper的事务ID,由64位数字组成,分为高32位和低32位

高32位:Epoch周期数,值为最新的领导的对应的id,其实就是就是一个递增的数字

低32位:计数器,一个递增的计数器,当处理了一个事务,值+1

zxid生成的规则:ZxidUtils.makeZxid

public class ZxidUtils {
 
    public static long getEpochFromZxid(long zxid) {
        return zxid >> 32L;
    }
    public static long getCounterFromZxid(long zxid) {
        return zxid & 0xffffffffL;
    }
    public static long makeZxid(long epoch, long counter) {
        return (epoch << 32L) | (counter & 0xffffffffL);
    }
    public static String zxidToString(long zxid) {
        return Long.toHexString(zxid);
    }
 
}

Zookeeper节点数据操作流程

183233-20160316223234865-1124736424

详细说明

  1. 在Client向Follwer发出一个写的请求
  2. Follwer把请求发送给Leader
  3. Leader接收到以后开始发起投票并通知Follwer进行投票
  4. Follwer把投票结果发送给Leader
  5. Leader将结果汇总后如果需要写入,则开始写入同时把写入操作通知给Leader然后commit;
  6. Follwer把请求结果返回给Client

Zookeeper集群的两种运行状态

leader可用状态

image-20210228144111968

leader不可用状态

image-20210228144131837

不可用状态恢复到可用状态根据官方测试小于200ms

Zookeeper的集群选举

Leader选举是Zookeeper中最重要的技术之一,也是保证分布式数据一致性的关键所在。

zookeeper集群搭建及选举模式

服务器启动时期的选举

服务器启动时期的Leader选举

Leader选举的时候,需要注意的是,隐式条件便是Zookeeper的集群规模至少是2台机器,这里我们以3台机器组成的服务器集群为例。在服务器集群初始化阶段,当有一台服务器(我们假设这台机器的myid为1,因此称其为Server1)启动的时候,它是无法完成Leader选举的。当第二胎机器(同样,我们假设这台服务器的myid是2,称其为Server2)也启动后,此时这两台机器已经能够进行互相通信,每台机器都试图找到一个Leader,于是便进入了Leader选举流程。

  1. 每个Server会发出一个投票,由于是初始情况,因此对于Server1和Server2来说,都会将自己作为Leader服务器来进行投票,每次投票包含最基本的元素有:所推举的服务器的myid和ZXID,我们以(myid,ZXID)的形式来表示。因为是初始化阶段,因此无论是Server1还是Server2,都会投给自己,即Server1的投票为(1,0),Server2的投票为(2,0),然后各自将这个投票发给集群中其他所有机器。

  2. 接收来自各个服务器的投票,每个服务器都会接收来自其他服务器的投票。集群中的每个服务器在接收到投票后,首先会判断该投票的有效性,包括检查是否是本轮投票,是否来自LOOKING状态的服务器。

  3. 处理投票,在接收到来自其他服务器的投票后,针对每一个投票,服务器都需要将别人的投票和自己的投票进行PK,PK的步骤及处理流程如下:

    1. 统计投票,每次投票后,服务器都会统计所有投票,判断是否已经有过半的及其接收到相同的投票信息。对于Server1和Server2服务器来说,都统计出集群中已经有两台机器接受了(2,0)这个投票信息。这里我们需要对“过半”的概念做一个简单的介绍。所有“过半”就是指大于集群机器数量的一半,即大于或等于(n/2+1)。对于这里由3台机器构成的集群,大于等于2台即为达到“过半”要求。,当选举票数机器相同

      优先检查ZXID,ZXID比较大的服务器优先作为Leader
      
      如果ZXID相同的话,那么就比较myid。myid比较大的服务器作为Leader服务器
      
    2. 改变服务器状态,一旦确定Leader,每个服务器就会更新自己的状态。如果是Follower,那么就变更为FOLLOWING,如果是Leader,那么就变更为LEADING。

服务器运行时期的Leader选举

在Zookeeper集群正常运行过程中,一旦选出一个Leader,那么所有服务器的集群角色一般不会再发生变化,也就是说,Leader服务器将一直作为集群的Leader,即使集群中有非Leader挂了或有新机器加入集群也不会影响Leader。但是一旦Leader所在机器挂了,那么整个集群将暂时无法对外提供服务,而是进入新一轮的Leader选举。服务器运行期间的Leader选举和启动时期的Leader选举基本过程一致的。

Zookeeper的Watch

Zookeeper提供了数据的发布/订阅功能,多个订阅者可同时监听某一特定主题对象,当该主题对象的自身状态发生变化时(例如节点内容改变、节点下的子节点列表改变等),会实时、主动通知所有订阅者。

Zookeeper采用了Watcher机制实现数据的发布/订阅功能。该机制在被订阅对象发生变化时会异步通知客户端,因此客户端不必在Watcher注册后轮询阻塞,从而减轻了客户端压力。

Watcher机制实际上与观察者模式类似,也可看作是一种观察者模式在分布式场景下的实现方式。

watcher架构

Watcher实现由三个部分组成:

  • Zookeeper服务端;
  • Zookeeper客户端;
  • 客户端的ZKWatchManager对象;

客户端首先将Watcher注册到服务端,同时将Watcher对象保存到客户端的Watch管理器中。当Zookeeper服务端监听的数据状态发生变化时,服务端会主动通知客户端,接着客户端的Watch管理器会触发相关Watcher来回调相应处理逻辑,从而完成整体的数据发布/订阅流程。

image-20210303104435501

img

此时小红旗是一个watcher,当小红旗被创建并注册到node1节点(会有相应的API实现)后,就会监听node1+node_a+node_b或node_a+node_b。这里两种情况是因为在创建watcher注册时会有多种途径。并且watcher不能监听到孙节点。注意注意注意,watcher设置后,一旦触发一次后就会失效,如果要想一直监听,需要在process回调函数里重新注册相同的 watcher

Watcher特性

特性 说明
一次性 Watcher是一次性的,一旦被触发就会移除,再次使用时需要重新注册
客户端顺序回调 Watcher回调是顺序串行化执行的,只有回调后客户端才能看到最新的数据状态。一个Watcher回调逻辑不应该太多,以免影响别的watcher执行
轻量级 WatchEvent是最小的通信单元,结构上只包含通知状态、事件类型和节点路径,并不会告诉数据节点变化前后的具体内容;
时效性 Watcher只有在当前session彻底失效时才会无效,若在session有效期内快速重连成功,则watcher依然存在,仍可接收到通知;

Zookeeper顺序一致性简单验证

  1. 启动三个1,2,3三个节点(共四个节点),三个节点已经可以选举出leader

    ./bin/zkServer.sh start-foreground

    节点1日志

    image-20210302121105538

    节点2日志

    image-20210302121316291

    节点3日志

    image-20210302123647739

  2. 启动节点4

  3. 连接3,4节点并新增数据,会发现cZxid会自增

    ./bin/zkCli.sh -server 127.0.0.1:12183

    ./bin/zkCli.sh -server 127.0.0.1:12184

    create -s /ooxx/xxx

    get -s /ooxx/xxx

    image-20210302124819564

    当进行修改时,会自动在mzxid(该数据节点被修改时最新的事务id)字段加1

    image-20210302125027621

    4.在此时中断leader进程,并在4节点继续新增相关数据

    mZxid前两次(剩余2个following节点)事务是进行了数据同步,第三次事务才是修改/ooxx节点数据

    image-20210302130252033

    5.不同客户端创建同一个节点

    leader会在此节点进行递增,不会覆盖创建,规避数据被覆盖

    image-20210302130554281

image-20210302130614931

当删除节点重新创建,但是数据名会继续递增(leade内部维护)

image-20210302130920351

Zookeeper的应用场景

  1. 统一的配置管理 ------------节点下可以存储1M的数据
  2. 分组管理 --------------Zookeeper的树结构,可以有多个节点
  3. 统一命名 -------------顺序一致性的能力(不会覆盖数据)
  4. 分布式同步 -------------临时节点
    • 分布式锁 ---------临时节点下放置分布式锁(session挂掉自动消失,就会释放锁)
    • 带事务(队列形式)的公平锁 --------------在持久的父节点下,建立多个同级同名(create -s 会自动的添加序列)的临时节点(可以持有多把锁),后面的锁盯住前面的锁(01-02-03)
    • HA,选主(hadoop)
  5. 发布订阅
posted @ 2021-06-08 14:34  知白守黑,和光同尘  阅读(218)  评论(0编辑  收藏  举报