大数据-zookeeper(上)

Zookeeper概述

  1. zookeeper的wiki
  2. zookeeper是一个开源的分布式的,为分布式应用提供协调服务的Apache项目
  3. zookeeper负责存储和管理大家都关心的数据,然后接受观察者的注册,一旦数据发生了变更,zookeeper就负责通知已经注册的那些观察者
  4. zookeeper是一主(leader)多从(follower)组成的集群
  5. 集群中只要有半数以上的节点存活,zookeeper就能正常提供服务
  6. 全局数据保持一致,每个server都保存一份相同的数据副本,client不论连接到那个server,数据都是一致的
  7. 来自同一个client的更新请求顺序执行
  8. 数据更新原子性,一次数据更新要么成功要么失败
  9. 实时性,在一定时间范围内,client能读到最新的数据

Zookeeper 数据结构

  1. Zookeeper的数据模型的结构和Unix文件系统类似,整体可以看做一棵树,每个节点称作一个ZNode,每个ZNode默认能够存储1MB的数据,每个ZNode都可以通过其路径唯一标识
  2. 应用场景
    (1) 统一命名服务
    (2) 统一配置管理: 一般一个集群中,所有的节点配置信息都是一致的,这时就可以交给zookeeper管理,将配置信息写入zookeeper上的一个ZNode,各个客户端服务器监听这个ZNode,一旦ZNode中的数据被修改,Zookeeper将会通知各个客户端
    (3) 统一集群管理: 可以将各个节点的信息写入Zookeeper的一个ZNode,监听这个Znode节点可以获取它的实时状态变化
    (4) 服务器上下线
    (5) 软负载均衡: 在zookeeper中记录每台服务器的访问数,让访问数最少的服务器去处理最新的客户端请求
    (6) 分布式锁和分布式队列

Zookeeper选举机制

  1. 半数机制: 集群中半数以上的机器存活,集群可用.所以Zookeeper适合安装奇数台服务器

  2. Zookeeper虽然没有在配置文件中指定Master和Follower,但是,Zookeeper工作时,是有个节点为Leader,其他则为Follower,Leader是通过内部选举机制临时产生的

  3. 举例
    此时有5台服务器组成zookeeper集群,id从1-5,然后依次启动
    在这里插入图片描述
    (1) server1启动,此时只有一台服务器启动,所以它的选举状态一直是LOOKING状态

    (2) server2启动,它和server1进行通信,互相交换选举结果,由于两者都没有历史数据,所以id值较大的server2胜出,但是由于没有达到超过半数以上的server同意选举server2,所以server1和server2 继续保持LOOKING状态

    (3) server3启动,和server1 server2进行通信,然后server3现在有三台服务器选举,所以server3现在是leader

    (4) server4启动,由于server3已经有三票,超过了半数票,所以server4只能是follower,server5和server4同理

  4. 详细过程(转载自: https://www.cnblogs.com/veblen/p/10992103.html)
    (1) serverid(服务器id,即myid) 编号越大在选择算法中的权重越大

    (2) zxid(最新数据id,即LastLoggedZxid)
    服务器中存放的最新数据id,id值越大说明数据越新,在选择算法中权重越大

    (3) epoch(逻辑时钟 即 PeerEpoch)
    每个服务器都会给自己投票,或者叫投票次数,同一轮投票过程中逻辑时钟是相同的,没投完一次票这个数据就会增加,然后与接收到的其他服务器返回的投票信息中的数值相比,如果收到低于当前轮次的投票结果,该投票无效,需更新到当前轮次和当前的投票结果

    (4) 选举状态
    LOOKING 竞选状态
    FOLLOWING 随从状态,同步leader状态,参与投票
    OBSERVING 观察状态,同步leader状态,不参与投票
    LEADING 领导者状态

    (5) 选举算法
    通过zoo.cfg配置文件中的electionAlg属性指定(1-3),要理解算法,需要一些paxos算法的理论基础.
    1对应: LeaderElection算法
    2对应: AuthFastLeaderElection算法
    3对应: FastLeaderElection默认的算法

    (6) 投票内容
    选举人id,选举人数据id,选举人选举轮数,选举人选举状态,推举人id,推举人选举轮数

    (7) Leader选举
    Leader选举有如下两种:
    ① 服务器初始化启动的Leader选举
    ② 服务器运行期间的Leader选举
    Leader选举的前提条件
    ① 只有服务器在LOOKING(竞选状态)才会去执行选举算法
    ② 集群规模至少是2台机器,才可以选举leader
    ③ 当一台服务器启动时不能选举的,等第二台服务器启动后,两台机器之间可以进行通信,才可以进行通信
    ④ 服务器运行期间无法和leader保持连接

    (8) 服务器启动期间的Leader选举
    在集群初始化阶段,当有一台服务器server1启动时,其单独无法进行和完成leader选举,当第二台服务器server2启动后,此时两台服务器可以相互通信,每台机器都试图找到leader,于是进入leader选举过程
    .
    ① 每个server发出一个投票给自己,由于是初始状态,server1和server2都会将自己作为leader服务器来进行投票,每次投票会包含锁推举的服务器的myid和Zxid,使用(myid,zxid)来表示,此时server1的投票为(1,0),server2的投票为(2,0),然后各自将这个投票发给集群中其他机器
    .
    ② 接收来自各个服务器的投票,集群的每个服务器收到投票后,首先判断该投票的有雄性,如检查是否是本轮投票,是否来自LOOKING状态的服务器
    .
    ③ 处理投票,针对每个投票,服务器都需要将别人的投票和自己的投票进行PK,规则如下
    -1- 优先检查zxid,zxid比较大的服务器优先作为leader
    -2- 如果zxid相同,那么就比较myid,myid较大的服务器作为leader服务器,对于server1来说,投票是(1,0),接收到server2的投票(2,0),首先会比较两者的zxid,都是0,再比较myid,此时server2的myid是2,最大,于是更新自己的投票为(2,0),然后重新投票,对于server2来说,不用更新自己的投票,只是再次向集群中所有机器发出上一次投票信息即可
    .
    ④ 统计投票,每次投票后,服务器都会统计投票信息,判断是否已经有过半的机器接受到相同的投票信息,对于server1和server2来说,都统计出集群中已经有两台机器接受了(2,0)的投票信息,此时便认为已经选出了leader.
    .
    ⑤ 改变服务器状态,一旦确定了leader,每个服务器就会更新自己的状态,如果是follower,那么就变更为FOLLOWING,如果是leader,那么就更新为LEADING

    (9) 服务器运行期间的Leader选举
    在zookeeper运行期间挂掉了,即便当前有非leader服务器宕机或者新加入,此时也不会影响leader,但是一旦leader服务器挂掉了,那么整个集群将暂停对外服务,进入新一轮leader选举,其过程和启动时期的leader选举过程基本一致,假设正在运行的有server1 server2 server3 三台服务器,当前leader是server2,某时刻leader挂掉了,此时开始leader选举.
    .
    ① 变更状态,leader挂掉后,剩下的非observer服务器都会将自己的服务器状态变更为LOOKING,然后开始进入Leader选举流程
    .
    ② 每个server会发出一个投票,在这个过程中,需要生成投票信息(myid,zxid),每个服务器上的zxid可能不同,假定server1的zxid为123,而server3的zxid为122,在第一轮投票中,server1和server3都会投自己,产生投票(1,123) (3,122),然后各自将投票发送给集群所有机器
    .
    ③ 接收来自各个服务器的投票
    .
    ④ 处理投票,判断zxid,server1的zxid为123,大于server3的122,所以server3更新自己的投票为(1,123),然后重新投票,server1不用更新
    .
    ⑤ 统计投票,此时对于server1 和server3来说,都接收到了(1,123)的投票信息,过了半数,此时认为选出了leader,就是server1
    .
    ⑥ 改变服务器状态

    (10) Leader选举算法分析
    在3.4.0后的zookeeper版本只保留了TCP版本的FastLeaderElection,当一台机器进入Leader选举时,当前集群可能会处于以下两种状态
    -1-当zookeeper集群中的一台服务器出现以下两种状态时,开始进入选举状态
    ① 服务器初始化启动
    ② 服务器运行期间无法和leader保持连接
    -2-而当一台服务器进入leader选举流程时,当前集群也可能会处于以下两种状态
    ① 集群中本来已将有个leader
    ② 集群中不存在leader
    .
    第一种已经存在leader的情况,一般是因为某台机器启动的比较晚,在其启动之前集群已经在正常工作,对这种情况,该机器试图去选举leader时,会被告知当前服务器leader信息,对于该机器而言,仅仅需要和leader机器建立起连接,并进行状态同步
    .
    此刻重点看集群leader不存在的情况下,如何进行leader选举.
    ① 此时集群所有的机器处于一种LOOKING的状态,正在寻找leader,当服务器处于这个状态时,会向集群中所有其他机器发送消息,我们称这个消息为"投票",在这个投票中包括两个最基本的信息,所推举的服务器的myid,zxid,分别表示了被推举服务器的唯一标识和事务id.假设zookeeper由5台机器组成,myid分别为12345,zxid分别为99988,此时myid为2的服务器为leader,某时刻,myid12的机器出现了故障,此时集群开始leader选举.
    第一次投票,无法检测集群中其他机器的状态信息,每台机器都将自己作为被推举的对象来进行投票,于是myid为3 4 5的机器投票分别为(3,9) (4,8) (5,8)
    .
    ② 变更投票
    每台机器发出自己的投票后,也会接收到集群中其他机器的投票,然后根据规则决定是否改变自己的投票,这个规则就是leader选举算法的核心,假设在epoch相同的情况下.
    规则如下:
    -1- 如果接收到的投票zxid大于自己的zxid,就认可收到的投票,然后将该票再次投出去
    -2-如果zxid小于自己的zxid,那么就坚持自己的投票,不做变更
    -3-如果zxid相同,那么就对比myid,如果myid大于自己的myid,那么认可这个投票,然后将这个投票发送出去
    -4-如果zxid相同,接收到的投票的myid小于自己的myid,那么坚持自己的投票,不做变更
    在这里插入图片描述
    ③ 确定leader
    zookeeper集群总机器台数为5台,那么半数为(5/2+1)=3
    此刻server3收到了3票(3,9),就成为了leader

    一些细节:
    投票(vote)在zookeeper中包含字段① id,被推举的leader的myid ② zxid,被推举的leader事务id ③ electionEpoch: 逻辑时钟,用来判断多个投票是否在同一轮选举周期中,该值在服务端是一个自增序列,每次进入新一轮的投票后,都会对该值进行加1操作④ peerEpoch: 被推举的leader的epoch⑤ State: 当前服务器的状态
    .
    QuorumCnxManager: 网络IO,每台服务器在启动过程中,会启动一个QuorumPeerManager,负责各台服务器之间的底层Leader选举过程中的网络通信.
    ① 消息队列. QuorumCnxManager内部维护了一系列的队列,用来保存接收到的消息以及消息的发送器,除接收队列外,其他队列都按照myid分组形成的队列集合,如一个集群中除了自身还有三台机器,那么就会为这三台机器分别创建一个发送队列,互不干扰.
    recvQueue:消息接收队列,用于存放那些从其他服务器接收到的消息.
    queueSendMap: 消息发送队列,用于保存那些待发送的消息,按照myid进行分组
    senderWorkerMap: 发送器集合,每个SenderWorker消息发送器,都对应一台远程zookeeper服务器,负责消息的发送,按照myid分组
    lastMessageSent:最近发送的消息,为每个myid保留最近发送过的一个消息
    .
    ② 建立连接
    为了能够互相投票,zookeeper集群中的所有机器都需要两两建立网络链接.QuorumCnxManager在启动时会创建一个ServerSocket来监听Leader选举的通信端口(默认为3888),开启监听后,zookeeper能够不断的接收到来自其他服务器的创建连接请求,在接收到其他服务器的TCP连接请求时,会进行处理,为了避免两台机器之间重复的创建TCP连接,zookeeper只允许myid大的服务器主动和其他机器建立连接,否则断开连接.在接收到创建连接请求后,服务器通过对比自己和远程服务器的myid值来判断是否接收连接请求,如果当前服务器发现自己的myid更大,那么会断开连接,然后自己主动和远程服务器连接,一旦建立连接,就会根据远程服务器的myid来创建响应的消息发送器SendWorker和消息接收器RecvWorker,并启动.
    .
    ③消息接收和发送
    消息接收: 有消息接收器RecvWorker负责,由于每个zookeeper为每个远程服务器都分配一个单独的RecvWorker,因此,每个RecvWorker只需要不断地从这个TCP连接中读取消息,并将其保存到recvQueue队列中.
    消息发送:由于zookeeper为每个远程服务器都分配一个单独的SendWorker,因此,每个SendWorker只需要不断的从对应消息发送队列中获取出一个消息发送即可,同时将这个消息放入lastMessageSent中,在SendWorker中,一旦zookeeper发现针对当前服务器的消息发送队列为空,那么此时需要从lastMessageSent中取出一个最近发送过的消息来进行再次发送,这是为了解决接收方在消息接收前或者接收到消息后服务器挂了,导致消息尚未被正确处理,同时,zookeeper能够保证接收方在处理消息时,会对重复消息进行正确的处理.

(11)FastLeaderElection:选举算法核心
外部投票:特指其他服务器发来的投票。
内部投票:服务器自身当前的投票。
选举轮次:Zookeeper服务器Leader选举的轮次,即logicalclock。
PK:对内部投票和外部投票进行对比来确定是否需要变更内部投票。

(1) 选票管理
sendqueue:选票发送队列,用于保存待发送的选票。

recvqueue:选票接收队列,用于保存接收到的外部投票。

WorkerReceiver:选票接收器。其会不断地从QuorumCnxManager中获取其他服务器发来的选举消息,并将其转换成一个选票,然后保存到recvqueue中,在选票接收过程中,如果发现该外部选票的选举轮次小于当前服务器的,那么忽略该外部投票,同时立即发送自己的内部投票。

WorkerSender:选票发送器,不断地从sendqueue中获取待发送的选票,并将其传递到底层QuorumCnxManager中。
(2)算法核心
在这里插入图片描述
上图展示了FastLeaderElection模块是如何与底层网络I/O进行交互的。Leader选举的基本流程如下

1、自增选举轮次。Zookeeper规定所有有效的投票都必须在同一轮次中,在开始新一轮投票时,会首先对logicalclock进行自增操作。

2、初始化选票。在开始进行新一轮投票之前,每个服务器都会初始化自身的选票,并且在初始化阶段,每台服务器都会将自己推举为Leader。

3、发送初始化选票。完成选票的初始化后,服务器就会发起第一次投票。Zookeeper会将刚刚初始化好的选票放入sendqueue中,由发送器WorkerSender负责发送出去。

4、接收外部投票。每台服务器会不断地从recvqueue队列中获取外部选票。如果服务器发现无法获取到任何外部投票,那么就会立即确认自己是否和集群中其他服务器保持着有效的连接,如果没有连接,则马上建立连接,如果已经建立了连接,则再次发送自己当前的内部投票。

5、判断选举轮次。在发送完初始化选票之后,接着开始处理外部投票。在处理外部投票时,会根据选举轮次来进行不同的处理。

外部投票的选举轮次大于内部投票。若服务器自身的选举轮次落后于该外部投票对应服务器的选举轮次,那么就会立即更新自己的选举轮次(logicalclock),并且清空所有已经收到的投票,然后使用初始化的投票来进行PK以确定是否变更内部投票。最终再将内部投票发送出去。

外部投票的选举轮次小于内部投票。若服务器接收的外选票的选举轮次落后于自身的选举轮次,那么Zookeeper就会直接忽略该外部投票,不做任何处理,并返回步骤4。

外部投票的选举轮次等于内部投票。此时可以开始进行选票PK。

6、选票PK。在进行选票PK时,符合任意一个条件就需要变更投票。

若外部投票中推举的Leader服务器的选举轮次大于内部投票,那么需要变更投票。

若选举轮次一致,那么就对比两者的ZXID,若外部投票的ZXID大,那么需要变更投票。

若两者的ZXID一致,那么就对比两者的SID,若外部投票的SID大,那么就需要变更投票。

7、变更投票。经过PK后,若确定了外部投票优于内部投票,那么就变更投票,即使用外部投票的选票信息来覆盖内部投票,变更完成后,再次将这个变更后的内部投票发送出去。

8、选票归档。无论是否变更了投票,都会将刚刚收到的那份外部投票放入选票集合recvset中进行归档。recvset用于记录当前服务器在本轮次的Leader选举中收到的所有外部投票(按照服务队的SID区别,如{(1, vote1), (2, vote2)…})。

9、统计投票。完成选票归档后,就可以开始统计投票,统计投票是为了统计集群中是否已经有过半的服务器认可了当前的内部投票,如果确定已经有过半服务器认可了该投票,则终止投票。否则返回步骤4。

10、更新服务器状态。若已经确定可以终止投票,那么就开始更新服务器状态,服务器首选判断当前被过半服务器认可的投票所对应的Leader服务器是否是自己,若是自己,则将自己的服务器状态更新为LEADING,若不是,则根据具体情况来确定自己是FOLLOWING或是OBSERVING。

以上10个步骤就是FastLeaderElection的核心,其中步骤4-9会经过几轮循环,直到有Leader选举产生。

Zookeeper节点,监听器,写数据

  • 节点类型
    持久(Persistent) : 客户端和服务器断开连接后,创建的节点不删除
    短暂(Ephemeral): 客户端和服务器断开连接后,创建的节点自己删除
    说明: 创建znode时设置顺序标识,znode名称后会附加一个值,顺序号是一个单调递增的计数器,由父节点维护,在分布式系统中,顺序号可以被用于为所有的事件进行全局排序,这样客户端可以通过顺序号来推断事件的顺序
    (1) 持久化目录节点
    客户端与zookeeper断开连接后,该节点依旧存在
    (2) 持久化顺序编号目录节点
    客户端与zookeeper断开连接后,该节点依旧存在,只是zookeeper给该节点进行顺序编号
    (3) 临时目录节点
    客户端与zookeeper断开连接后,该节点删除
    (4) 临时顺序编号目录节点
    客户端与zookeeper断开连接后,该节点被删除,只是zookeeper给该节点名称进行顺序编号

  • stat结构体
    ① czxid-创建节点的事务id
    每次修改zookeeper状态都会收到一个zxid形式的时间戳,也就是zookeeper的事务id,事务id是zookeeper中所有修改总的次序,每个修改都有唯一的zxid,如果zxid1小于zxid2,那么zxid1在zxid2之前发生
    ② ctime-znode被创建的毫秒数(从1970开始)
    ③ mzixd-znode最后更新的事务zxid
    ④ mtime-znode最后修改的毫秒数
    ⑤ pZxid-znode最后更新的子节点zxid
    ⑥ cversion-znode子节点变化号,znode子节点修改次数
    ⑦ dataversion -znode数据变化号
    ⑧ aclVersion - znode访问控制列表的变化号
    ⑨ ephemeralOwner -如果是临时节点,这个是znode拥有者的session id,如果不是临时节点,那么这个值就是 0
    ⑩ dataLength -znode的数据长度
    ⑾ numChildren -znode子节点数量

  • Zookeeper监听器
    ① 在main()线程里创建zookeeper客户端,这时就会创建两个线程,一个负责网络连接通信(connect),一个负责监听(listener)
    ② 通过connect线程将注册的监听事件发送给zookeeper
    ③ 在zookeeper的注册监听器列表中将注册的监听器事件添加到列表里
    ④ zookeeper监听到有数据变化或路径变化,就会将这个消息发送给listener线程
    ⑤ listener线程内部调用了process()方法
    在这里插入图片描述

  • 写数据流程
    在这里插入图片描述

©️2020 CSDN 皮肤主题: 书香水墨 设计师:CSDN官方博客 返回首页