Zookeeper原理

Zookeeper概述

Zookeeper 是一个开源的分布式协调服务框架 ,主要用来解决分布式集群中应用系统的一致性问题和数据管理问题。
从设计模式角度来理解:是一个基于观察者模式设计的分布式服务管理框架,它负责存储和管理大家都关心的数据,然后接受观察者的注册,一旦这些数据的状态发生变化,Zookeeper就将负责通知已经在Zookeeper上注册的那些观察者做出相应的反应。

Zookeeper特点

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

Zookeeper数据结构

Zookeeper数据模型的结构与Unix文件系统很类似,整体上可以看作一棵树,每个节点称作一个ZNode,每一个ZNode默认能够存储1MB的数据,每个ZNode都可以通过其路径唯一标识。
与文件系统不同的是,这些节点都可以设置关联的数据,而文件系统中只有文件节点可以存放数据而目录节点不行。
Zookeeper 为了保证高吞吐和低延迟,在内存中维护了这个树状的目录结构,这种特性使得 Zookeeper 不能用于存放大量的数据,每个节点的存放数据上限为1M。

Zookeeper应用场景

提供的服务包括:统一命名服务、统一配置管理、统一集群管理、服务器节点动态上下线、软负载均衡等。

  1. 一、统一命名服务:在分布式环境下,经常需要对应用/服务进行统一命名,便于识别。例如:IP不容易记住,而域名容易记住
  2. 二、统一配置管理:
  3. 1、分布式环境下,配置文件同步非常常见:
    • 1、一般要求一个集群中,所有的节点的配置信息是一致的,比如Kafka集群。
    • 2、对配置文件修改后,希望能够快速同步到各个节点上。
  4. 2、配置管理可交由Zookeeper实现:
    • 1、可将配置信息写入Zookeeper上的一个ZNode。
    • 2、各个客户端服务器监听这个ZNode。
    • 3、一旦ZNode中的数据被修改,Zookeeper将通知各个客户端服务器。
  5. 三、统一集群管理:
  6. 1、分布式环境中,实时掌握每个节点的状态是必要的,可以根据节点实时状态做出一些调整。
  7. 2、Zookeeper可以实现实时监控节点状态变化
    - 1、可将节点信息写入Zookeeper上的一个ZNode.
    - 2、监听这个ZNode可以获得它的实时状态变化。
  8. 四、服务动态上下线:客户端能实时洞察到服务器上下线的变化
  9. 五、软负载均衡:在Zookeeper中记录每台服务器的访问数,让访问数最少的服务器去处理最新的客户请求。

Zookeeper选举机制

Leader选举是保证分布式数据一致性的关键所在。当Zookeeper集群中的一台服务器出现以下两种情况之一时,需要进入Leader选举。

一、服务器启动时期的Leader选举

若进行Leader选举,则至少需要两台机器,这里选取3台机器组成的服务器集群为例。在集群初始化阶段,当有一台服务器Server1启动时,其单独无法进行和完成Leader选举,当第二台服务器Server2启动时,此时两台机器可以相互通信,每台机器都试图找到Leader,于是进入Leader选举过程。选举过程如下

  1. 每个Server发出一个投票:由于是初始情况,Server1和Server2都会将自己作为Leader服务器来进行投票,每次投票会包含所推举的服务器的myid和ZXID,使用(myid, ZXID)来表示,此时Server1的投票为(1, 0),Server2的投票为(2, 0),然后各自将这个投票发给集群中其他机器。
  2. 接受来自各个服务器的投票:集群的每个服务器收到投票后,首先判断该投票的有效性,如检查是否是本轮投票、是否来自LOOKING状态的服务器。
  3. 处理投票:针对每一个投票,服务器都需要将别人的投票和自己的投票进行PK,PK规则如下:1、优先检查ZXID:ZXID比较大的服务器优先作为Leader。2、如果ZXID相同,那么就比较myid,myid较大的服务器作为Leader服务器。
    比如:对于Server1而言,它的投票是(1, 0),接收Server2的投票为(2, 0),首先会比较两者的ZXID,均为0,再比较myid,此时Server2的myid最大,于是更新自己的投票为(2, 0),然后重新投票,对于Server2而言,其无须更新自己的投票,只是再次向集群中所有机器发出上一次投票信息即可。
  4. 统计投票:每次投票后,服务器都会统计投票信息,判断是否已经有过半机器接受到相同的投票信息,对于Server1、Server2而言,都统计出集群中已经有两台机器接受了(2, 0)的投票信息,此时便认为已经选出了Leader。
  5. 改变服务器状态:一旦确定了Leader,每个服务器就会更新自己的状态,如果是Follower,那么就变更为FOLLOWING,如果是Leader,就变更为LEADING。
二、服务器运行时期的Leader选举

在Zookeeper运行期间,Leader与非Leader服务器各司其职,即便当有非Leader服务器宕机或新加入,此时也不会影响Leader,但是一旦Leader服务器挂了,那么整个集群将暂停对外服务,进入新一轮Leader选举,其过程和启动时期的Leader选举过程基本一致过程相同。

每个服务器的角色

每个服务器承担如下三种角色中的一种

  1. Leader:一个Zookeeper集群同一时间只会有一个实际工作的Leader,它会发起并维护与各Follwer及Observer间的心跳。所有的写操作必须要通过Leader完成再由Leader将写操作广播给其它服务器。
  2. Follower:一个Zookeeper集群可能同时存在多个Follower,它会响应Leader的心跳。Follower可直接处理并返回客户端的读请求,同时会将写请求转发给Leader处理,并且负责在Leader处理写请求时对请求进行投票。
  3. Observer:角色与Follower类似,但是无投票权。

节点类型

  1. PERSISTENT-持久节点:除非手动删除,否则客户端和服务器端断开连接后,创建的节点不删除。
  2. EPHEMERAL-临时节点:临时节点的生命周期与客户端会话绑定,一旦客户端会话失效(客户端与zookeeper 连接断开不一定会话失效),那么这个客户端创建的所有临时节点都会被移除。
  3. PERSISTENT_SEQUENTIAL-持久顺序节点:基本特性同持久节点,只是增加了顺序属性,节点名后边会追加一个由父节点维护的自增整型数字。
  4. EPHEMERAL_SEQUENTIAL-临时顺序节点:基本特性同临时节点,增加了顺序属性,节点名后边会追加一个由父节点维护的自增整型数字。

Znode结构

Znode是由三个部分构成

  • stat: 状态, Znode的权限信息, 版本等
  • data: 数据, 每个Znode都是可以携带数据的, 无论是否有子节点
  • children: 子节点列表

Stat结构体

  1. czxid-创建节点的事务zxid:每次修改ZooKeeper状态都会收到一个zxid形式的时间戳,也就是ZooKeeper事务ID。事务ID是ZooKeeper中所有修改总的次序。每个修改都有唯一的zxid,如果zxid1小于zxid2,那么zxid1在zxid2之前发生。
  2. ctime - znode被创建的毫秒数(从1970年开始)
  3. mzxid - znode最后更新的事务zxid
  4. mtime - znode最后修改的毫秒数(从1970年开始)
  5. pZxid-znode最后更新的子节点zxid
  6. cversion - znode子节点变化号,znode子节点修改次数
  7. dataversion - znode数据变化号
  8. aclVersion - znode访问控制列表的变化号
  9. ephemeralOwner- 如果是临时节点,这个是znode拥有者的session id。如果不是临时节点则是0。
  10. dataLength- znode的数据长度
  11. numChildren - znode子节点数量

监听器原理

监听原理详解:
Zookeeper 允许客户端向服务端的某个 Znode 注册一个 Watcher 监听,当服务端的一些指定事件触发了这个 Watcher,服务端会向指定客户端发送一个事件通知来实现分布式的通知功能,然后客户端根据 Watcher 通知状态和事件类型做出业务上的改变。

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

Watcher 特性总结:
7. 一次性:无论是服务端还是客户端,一旦一个 Watcher 被 触 发 ,Zookeeper 都会将其从相应的存储中移除。这样的设计有效的减轻了服务端的压力,不然对于更新非常频繁的节点,服务端会不断的向客户端发送事件通知,无论对于网络还是服务端的压力都非常大。
8. 轻量:
9. Watcher 通知非常简单,只会告诉客户端发生了事件,而不会说明事件的具体内容;
10. 客户端向服务端注册 Watcher 的时候,并不会把客户端真实的 Watcher 对象实体传递到服务端,仅仅是在客户端请求中使用 boolean 类型属性进行了标记。
11. watcher event 异步发送 :watcher 的通知事件从 server 发送到 client 是异步的,这就存在一个问题,不同的客户端和服务器之间通过 socket 进行通信,由于网络延迟或其他因素导致客户端在不同的时刻监听到事件,由于 Zookeeper 本身提供了 ordering guarantee,即客户端监听事件后,才会感知它所监视 znode发生了变化。所以我们使用 Zookeeper 不能期望能够监控到节点每次的变化。Zookeeper 只能保证最终的一致性,而无法保证强一致性
12. 注册 watcher getData、exists、getChildren
13. 触发 watcher create、delete、setData
14. 当一个客户端连接到一个新的服务器上时,watch 将会被以任意会话事件触发。当与一个服务器失去连接的时候,是无法接收到 watch 的。而当 client 重新连接时,如果需要的话,所有先前注册过的 watch,都会被重新注册。通常这是完全透明的。只有在一个特殊情况下,watch 可能会丢失:对于一个未创建的 znode的 exist watch,如果在客户端断开连接期间被创建了,并且随后在客户端连接上之前又删除了,这种情况下,这个 watch 事件可能会被丢失。

写数据流程

  1. Client向Zookeeper的Server1上写数据,发送一个写请求。
  2. 如果Server1不是Leader,那么Server1会把接收到的请求进一步转发给Leader。因为每个zookeeper的Server里面有一个是Leader,这个Leader会将写请求广播给每个Server,各个Server写成功后就会通知Leader.
  3. 当Leader收到大多数Server数据写成功了,就说明数据写成功了。如果有3个节点,只要有2个节点数据写成功了,那么数据就写成功了。写数据成功后,Leader会告诉Server1数据写成功了。
  4. Server1会进一步通知Client数据写成功了,这时就认为整个写操作成功。

zookeeper数据同步

整个集群完成Leader选举后,Learner会向Leader进行注册,当Learner向Leader完成注册后,就进入数据同步环节,同步过程就是Leader将那些没有在Learner服务器上提交过的事务请求同步给Learner服务器,大体过程如下:
在这里插入图片描述

  1. 获取Learner状态。在注册Learner的最后阶段,Learner服务器会发送给Leader服务器一个ACKEPOCH数据包,Leader会从这个数据包中解析出该Learner的currentEpoch和lastZxid。
  2. 数据同步初始化。首先从Zookeeper内存数据库中提取出事务请求对应的提议缓存队列proposals,同时完成peerLastZxid(该Learner最后处理的ZXID)、minCommittedLog(Leader提议缓存队列commitedLog中最小的ZXID)、maxCommittedLog(Leader提议缓存队列commitedLog中的最大ZXID)三个ZXID值的初始化。

对于集群数据同步而言,通常分为四类,直接差异化同步(DIFF同步)、先回滚再差异化同步(TRUNC+DIFF同步)、仅回滚同步(TRUNC同步)、全量同步(SNAP同步),在初始化阶段,Leader会优先以全量同步方式来同步数据。同时,会根据Leader和Learner之间的数据差异情况来决定最终的数据同步方式。

  1. 直接差异化同步(DIFF同步,peerLastZxid介于minCommittedLog和maxCommittedLog之间)。Leader首先向这个Learner发送一个DIFF指令,用于通知Learner进入差异化数据同步阶段,Leader即将把一些Proposal同步给自己,针对每个Proposal,Leader都会通过发送PROPOSAL内容数据包和COMMIT指令数据包来完成。
  2. 先回滚再差异化同步(TRUNC+DIFF同步,Leader已经将事务记录到本地事务日志中,但是没有成功发起Proposal流程)。当Leader发现某个Learner包含了一条自己没有的事务记录,那么就需要该Learner进行事务回滚,回滚到Leader服务器上存在的,同时也是最接近于peerLastZxid的ZXID。
  3. 仅回滚同步(TRUNC同步,peerLastZxid大于maxCommittedLog)。Leader要求Learner回滚到ZXID值为maxCommittedLog对应的事务操作。
  4. 全量同步(SNAP同步,peerLastZxid小于minCommittedLog或peerLastZxid不等于lastProcessedZxid)。Leader无法直接使用提议缓存队列和Learner进行同步,因此只能进行全量同步。Leader将本机的全量内存数据同步给Learner。Leader首先向Learner发送一个SNAP指令,通知Learner即将进行全量同步,随后,Leader会从内存数据库中获取到全量的数据节点和会话超时时间记录器,将他们序列化后传输给Learner。Learner接收到该全量数据后,会对其反序列化后载入到内存数据库中。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值