Zookeeper学习笔记

zookeeper入门

概述

  • zookeeper是一个开源的分布式的,为分布式应用提供协调服务的 Apache 项目。
  • zookeeper从设计模式角度来理解:是一个基于观察者模式设计的分布式服务管理框架,它负责存储和管理大家都关心的数据,然后接受观察者的注册,一旦这些数据的状态发生变化,zookeeper就将负责通知已经在zookeeper上注册的哪些观察者做出相应的反应。

特点

  • Zookeeper:一个领导者(Leader),多个跟随者(Follower)组成的集群
  • 集群中只要有半数以上的节点存活,zookeeper集群就能正常服务
  • 全局数据一致:每个Server保存一份相同的数据副本,Client无论链接到哪个Server,数据都是一致的
  • 更新请求顺序进行,来同一个Client的更新请求按其发送顺序依次执行
  • 数据更新原子性,依次数据更新要么成功,要么失败
  • 实时性,在一定时间范围内,Client能读到的最新数据

数据结构

  • Zookeeper数据模型的结构与Unix文件系统很类似,整体上可以看做是一棵树,每个节点乘坐一个ZNode。每个ZNode默认能够存储1MB的数据,每个ZNode都可以通过其路径唯一标识

应用场景

  • 提供的服务包括:
    • 统一命名服务

    • 统一配置管理

    • 统一集群管理

    • 服务器节点动态上下线

    • 软负载均衡

    • 下载地址: https://mirrors.tuna.tsinghua.edu.cn/apache/zookeeper/

Zookeeper 的内部原理

选举机制(面试重点)

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

简单举例:

目前有5台服务器,每台服务器均没有数据,它们的编号分别是1,2,3,4,5,按编号依次启动,它们的选择举过程如下:
- 服务器1启动,给自己投票,然后发投票信息,由于其它机器还没有启动所以它收不到反馈信息,服务器1的状态一直属于Looking(选举状态)。
- 服务器2启动,给自己投票,同时与之前启动的服务器1交换结果,由于服务器2的编号大所以服务器2胜出,但此时投票数没有大于半数,所以两个服务器的状态依然是LOOKING。
- 服务器3启动,给自己投票,同时与之前启动的服务器1,2交换信息,由于服务器3的编号最大所以服务器3胜出,此时投票数正好大于半数,所以服务器3成为领导者,服务器1,2成为小弟。
- 服务器4启动,给自己投票,同时与之前启动的服务器1,2,3交换信息,尽管服务器4的编号大,但之前服务器3已经胜出,所以服务器4只能成为小弟。
- 服务器5启动,后面的逻辑同服务器4成为小弟。

选择机制中的概念

1、Serverid:服务器ID
比如有三台服务器,编号分别是1,2,3。
编号越大在选择算法中的权重越大。
2、Zxid:数据ID
服务器中存放的最大数据ID.
值越大说明数据越新,在选举算法中数据越新权重越大。
3、Epoch:逻辑时钟
或者叫投票的次数,同一轮投票过程中的逻辑时钟值是相同的。每投完一次票这个数据就会增加,然后与接收到的其它服务器返回的投票信息中的数值相比,根据不同的值做出不同的判断。
4、Server状态:选举状态
* LOOKING,竞选状态。
* FOLLOWING,随从状态,同步leader状态,参与投票。
* OBSERVING,观察状态,同步leader状态,不参与投票。
* LEADING,领导者状态。
- 选举消息内容
在投票完成后,需要将投票信息发送给集群中的所有服务器,它包含如下内容。
* 服务器ID
* 数据ID
* 逻辑时钟
* 选举状态
- 选举流程图
因为每个服务器都是独立的,在启动时均从初始状态开始参与选举,下面是简易流程图。

选举状态图

描述Leader选择过程中的状态变化,这是假设全部实例中均没有数据,假设服务器启动顺序分别为:A,B,C。

  • 判断是否已经胜出
    默认是采用投票数大于半数则胜出的逻辑。

选举流程详述

一、首先开始选举阶段,每个Server读取自身的zxid。
二、发送投票信息
a、首先,每个Server第一轮都会投票给自己。
b、投票信息包含 :所选举leader的Serverid,Zxid,Epoch。Epoch会随着选举轮数的增加而递增。
三、接收投票信息
1、如果服务器B接收到服务器A的数据(服务器A处于选举状态(LOOKING 状态)
1)首先,判断逻辑时钟值:
    a)如果发送过来的逻辑时钟Epoch大于目前的逻辑时钟。首先,更新本逻辑时钟Epoch,同时清空本轮逻辑时钟收集到的来自其他server的选举数据。然后,判断是否需要更新当前自己的选举leader Serverid。判断规则rules judging:保存的zxid最大值和leader Serverid来进行判断的。先看数据zxid,数据zxid大者胜出;其次再判断leader Serverid,leader Serverid大者胜出;然后再将自身最新的选举结果(也就是上面提到的三种数据(leader Serverid,Zxid,Epoch)广播给其他server)
    b)如果发送过来的逻辑时钟Epoch小于目前的逻辑时钟。说明对方server在一个相对较早的Epoch中,这里只需要将本机的三种数据(leader Serverid,Zxid,Epoch)发送过去就行。
    c)如果发送过来的逻辑时钟Epoch等于目前的逻辑时钟。再根据上述判断规则rules judging来选举leader ,然后再将自身最新的选举结果(也就是上面提到的三种数据(leader Serverid,Zxid,Epoch)广播给其他server)。
2)其次,判断服务器是不是已经收集到了所有服务器的选举状态:若是,根据选举结果设置自己的角色(FOLLOWING还是LEADER),退出选举过程就是了。
最后,若没有收到没有收集到所有服务器的选举状态:也可以判断一下根据以上过程之后最新的选举leader是不是得到了超过半数以上服务器的支持,如果是,那么尝试在200ms内接收一下数据,如果没有新的数据到来,说明大家都已经默认了这个结果,同样也设置角色退出选举过程。
2、 如果所接收服务器A处在其它状态(FOLLOWING或者LEADING)。
    a)逻辑时钟Epoch等于目前的逻辑时钟,将该数据保存到recvset。此时Server已经处于LEADING状态,说明此时这个server已经投票选出结果。若此时这个接收服务器宣称自己是leader, 那么将判断是不是有半数以上的服务器选举它,如果是则设置选举状态退出选举过程。
    b) 否则这是一条与当前逻辑时钟不符合的消息,那么说明在另一个选举过程中已经有了选举结果,于是将该选举结果加入到outofelection集合中,再根据outofelection来判断是否可以结束选举,如果可以也是保存逻辑时钟,设置选举状态,退出选举过程。

  • 节点类型
  1. 持久(Persistent):客户端和服务器端断开连接后,创建的节点不删除
    1. 持久化目录节点:客户端与zk断开连接后,该节点依旧存在,/znode1,(Persistent)
    2. 持久化顺序编号目录节点:客户端与zk断开连接后,该节点依旧存在,只是zk给该节点名称进行顺序编号,/znode2_001,(Persistent_sequential)
  2. 短暂(Ephemeral):客户端与服务器端断开连接后,创建的节点自己删除
    1. 临时目录节点:客户端与zk断开连接后,该节点被删除,/znode3,(Ephemeral)
    2. 临时顺序编号目录节点:客户端与zk断开连接后,该节点被删除,只是zk给该节点名称进行顺序编号,/znode4_001,(Ephemeral_sequential)

监听器原理(面试重点)

原理详解
1. 首先有个main()线程
2. 在main线程中创建Zookeeper客户端,这时就会创建两个线程,一个负责网络连接通信(connet),一个负责监听(listener)
3. 通过connect线程讲注册的监听事件发送给Zookeeper
4. 在Zookeeper的注册监听器列表中奖注册的监听事件添加到列表中
5. Zookeeper监听到有数据或路径变化,就会将这个消息发送给listener线程
6. listener线程内部调用process()方法

  • 写数据流程
  1. Client向Zookeeper的Server1 上写数据,发送一个写请求
  2. 如果Server1 不是Leader,Server1 会把接收到的请求进一步转发给Leader ,因为每个Zookeeper的Server里有个Leader,这个Leader会将写请求广播给各个Server,比如Server1和Server2,各个Server写成功后会通知Leader
  3. 当Leader收到大多数Server数据写成功了,就说明数据写成功了。如果这三个结点有两个写成功了,就认为数据写成功。写成功后Leader会告诉Server1数据写成功了。
  4. Server1 会通知 Client 数据写成功,这是才认为整个写操作成功。