zookeeper深入了解

zookeeper深入了解,第1张

1. 什么是zookeeper

ZooKeeper是一个分布式的,开放源码的分布式应用程序协调服务,是Google的Chubby一个开源的实现,是Hadoop和Hbase的重要组件。

基于ZooKeeper的数据结构,Watcher,选举机制等可以实现数据的发布/订阅,软负载均衡,命名服务,统一配置管理,分布式锁,集群管理等功能。 

Zookeeper 有三种运行模式:单机模式、伪集群模式和集群模式。

单机模式:这种模式一般适用于开发测试环境,一方面我们没有那么多机器资源,另外就是平时的开发调试并不需要极好的稳定性。

集群模式:一个 ZooKeeper 集群通常由一组机器组成,一般 3 台以上就可以组成一个可用的 ZooKeeper 集群了。组成 ZooKeeper 集群的每台机器都会在内存中维护当前的服务器状态,并且每台机器之间都会互相保持通信。

伪集群模式:这是一种特殊的集群模式,即集群的所有服务器都部署在一台机器上。当你手头上有一台比较好的机器,如果作为单机模式进行部署,就会浪费资源,这种情况下,ZooKeeper 允许你在一台机器上通过启动不同的端口来启动多个 ZooKeeper 服务实例,以此来以集群的特性来对外服务。

2. 为什么要使用zookeeper 2.1 利用zookeeper做全局配置管理

在实际业务系统开发中,会通过配置属性让业务逻辑更加丰富。常采用的方案包括:

  • 将配置文件写入数据库,通过读取数据库更新配置;
  • 利用Redis缓存做中间数据存储;
  • 通过zookeeper做配置管理,将配置属性写入zookeeper;

为什么选用zookeeper做配置中心呢?主要是当配置属性发生变化的时候,期望对应的业务系统能第一时间感知到,做对应属性的更新。zookeeper有强一致性保障,数据容错性更强;更重要的是zookeeper自带的watch机制,非常适合当有数据变化时触发,数据库和redis都无法直接提供此能力。

2.2 统一命名服务 2.3 分布式锁

zookeeper致力于提供高性能、高可用、顺序一致性的分布式协调服务,保证数据最终一致性;主要实现包括:

  • 高性能(简单的数据模型)

    • 采用树形结构组织数据节点;
    • 全量数据节点都存储在内存中;
    • Follower 和 Observer 直接处理非事务请求;
  • 高可用(构建集群)
    • 半数以上机器存活,服务就能正常运行
    • 自动进行 Leader 选举
  • 顺序一致性(事务 *** 作的顺序)
    • 每个事务请求,都会转发给 Leader 处理
    • 每个事务,会分配全局唯一的递增id(zxid,64位:epoch + 自增 id)
  • 最终一致性
    • 事务提交后,一定会被半数以上的follower节点看到
3. zookeeper数据结构 3.1 节点类型

ZooKeeper的数据结构,跟Unix文件系统非常类似,可以看做是一颗树,每个节点叫做ZNode。每一个节点可以通过路径来标识。结构如下:

 每个节点在 Zookeeper 中叫做 Znode,并且其有一个唯一的路径标识;节点 Znode 可以包含数据和子节点。Znode分为四种类型:

  • 临时(Ephemeral):客户端和服务端断开连接后,临时Znode节点会自动删除
  • 临时顺序(EPHEMERAL_SEQUENTIAL): 创建节点的时候有顺序,会依次递增
  • 持久(Persistent):客户端和服务端断开连接后,持久Znode节点不会删除。
  • 持久顺序(PERSISTENT_SEQUENTIAL): 是持久性节点一种,只是创建节点的时候每个节点有序,遵循单调递增;

EPHEMERAL 类型的节点不能有子节点;

3.2 数据访问

每个znode节点存储的数据都具有原子 *** 作性,也就是说这个节点的数据要么同时被写入,要么都没有写入;读数据的时候要么同时读到,要么都读不到。另外,每一个节点都拥有自己的ACL列表,限定了用户对目标节点可以执行的 *** 作,包括CREATE: 创建子节点的权限;READ: 获取节点数据和子节点列表的权限;WRITE: 更新节点数据的权限;DELETE: 删除子节点的权限;ADMIN: 设置节点ACL的权限。

注意:Znode 中的数据可以有多个版本,可以根据版本号访问对应节点数据。

4. zookeeper watcher监听机制

ZooKeeper允许用户在指定节点上注册一些Watcher,当数据节点发生变化(数据内容发生变化或者子节点增减变化)的时候,ZooKeeper服务器会把这个变化的通知发送给感兴趣的客户端。Watcher 机制包括三个角色:客户端线程、客户端的 WatchManager 以及 ZooKeeper 服务器。具体过程有:

  1. 客户端向 ZooKeeper 服务器注册一个 Watcher 监听,
  2. 把这个监听信息存储到客户端的 WatchManager 中
  3. 当 ZooKeeper 中的节点发生变化时,会通知客户端,客户端会调用相应 Watcher 对象中的回调方法。

客户端回调 Watcher:

客户端在和 ZooKeeper 建立连接时,会启动 sendThread 和 eventThread 线程。sendThread 线程负责发送请求给服务端,同时也接收服务端发送过来的响应,当它判断到响应中的 XID 标识为 -1,便将它作为一个通知类型的响应,将响应中的信息进行序列化,交给 eventThread 线程处理。eventThread 会根据响应内容判断该通知对应的 Watcher 类型,从 ZKWatchManager 中取出所有相关的 Watcher,然后放到 waitingEvents 队列中,该队列时一个待处理 Watcher 的队列,eventThrad 每次从中取出一个 Watcher,然后进行串行同步处理,就是依次调用队列中 Watcher 的 process 的方法。

Watcher 特性总结:

  • 一次性:不管是服务端还是客户端,当 Watcher 触发之后,就会将从本地内存中去除掉,如果还需要监听的话就需要反复注册。
  • 轻量:客户端发送给服务器的请求中只是表明该请求是对哪个路径的监听,服务端给客户端响应也只是告诉它监听的节点或子列表发生变化了,具体的变化信息需要重新去服务端获取,这个轻量的设计使得网络带宽和服务器的压力大大减小了。
5. zookeeper的选举机制

 zookeeper中存在三种服务器角色,分别是Leader,Follower以及Observer,其中Observer仅仅作为一个监控协调者的作用,并不参与zookeeper对外提供服务以及zookeeper的选举。

服务器节点状态(每个节点有且只有一个状态):

  • looking:寻找leader状态,当前集群没有leader,进入leader选举流程。
  • following:跟随者状态,接受leading节点同步和指挥。
  • leading:领导者状态。
  • observing:观察者状态,表名当前服务器是observer。

在讲选举过程之前,先介绍几个关键概念:

  • 事务编号zxid:zk每一次事务 *** 作都会产生唯一一个64位长度的zxid,其中前32是epoch,后32位是事务的计数序号;
  • 纪元epoch:每一次选主完成后,leader都会产出自己的epoch;
  • myid:zk节点在加入集群的时候,会配置节点服务器自由的一个ID号,默认是 /tmp/zookeeper/myid.myid 在整个集群中,各个节点之间是不能重复的.

ZAB(Zookeeper Atomic Broadcast),是为zk专门设计的一种支持崩溃恢复的原子广播协议,是Zookeeper保证数据一致性的核心算法。Zab借鉴了Paxos算法,但又不像Paxos那样,是一种通用的分布式一致性算法。

ZAB协议包括两种基本的模式,分别是:

  1. 消息原子广播(保证数据一致性)
  2. 崩溃恢复(解决2pc算法的单点问题)

整个集群启动过程中,或者当 Leader 服务器出现网络中断、崩溃退出或重启等异常时,ZAB协议就会进入崩溃恢复模式,选举产生新的Leader。当选举产生了新的 Leader,同时集群中有过半的机器与该 Leader 服务器完成了状态同步(即数据同步)之后,ZAB协议就会退出崩溃恢复模式,进入消息广播模式。这时,如果有一台遵守ZAB协议的服务器加入集群,因为此时集群中已经存在一个Leader服务器在广播消息,那么该新加入的服务器自动进入恢复模式:找到Leader服务器,并且完成数据同步。同步完成后,作为新的Follower一起参与到消息广播流程中。

进一步拆分,在崩溃恢复拆分为选举阶段和数据同步阶段,消息广播对应消息广播阶段。

5.1 选举阶段

服务启动期间的选举

  1. 每个节点都会对自己进行投票,然后把投票信息广播给集群中的其他节点
  2. 节点接收到其他节点的投票信息,然后和自己的投票进行比较,首先zxid较大的优先,如果zxid相同那么则会去选择myid更大者,此时大家都是LOOKING的状态
  3. 投票完成之后,开始统计投票信息,如果集群中过半的机器都选择了某个节点机器作为leader,那么选举结束
  4. 最后更新各个节点的状态,leader改为LEADING状态,follower改为FOLLOWING状态

服务运行期间的选举

如果选举出来的leader节点宕机了,那么运行期间就会重新进行leader的选举。

  1. leader宕机之后,非observer节点都会把自己的状态修改为LOOKING状态,然后重新进入选举流程
  2. 生成投票信息(myid,zxid),同样,第一轮的投票大家都会把票投给自己,然后把投票信息广播出去
  3. 接下来的流程和上面的选举是一样的,都会优先以zxid,然后选择myid,最后统计投票信息,修改节点状态,选举结束
5.2 数据同步阶段

整个同步过程主要是leader将自身proposal事务发送给从属于自身的大多数follower,在确认大多数follower接收到事务消息后,通知他们更新事务,保证follower和leader事务一致。

  1. 第一阶段 NewLeader:NewLeader 将新 epoch 和 S' 以 NewLeader(e', S')的消息形式发送给所有过半 (Quorum) 的 Follower。 S' 代表 NewLeader.history

  2. 第二阶段 Ack-LD:当 Follower 接收到 NewLeader(e', S') 消息后,

    1. 如果 Follower 的 epoch 等于 e',确定是该主的子民,Follower 将会执行事务应用 *** 作,将接收 S' 中的所有事务 Proposal,注意只是接收。
    2. 如果 Follower 的 epoch 不等于 e',即不是这一轮的 Follower,直接进入下一代循环。
    3. Leader 在接收到过半的 Follower 的 Ack-LD 消息后,发送 Commit 消息所有的 Follower,之后进入下一阶段即 Broadcast(消息广播)。
  3. 第三阶段 Commit-LD:在收到 Leader 的 Commit 消息后,按顺序依次调用 abdeliver() 处理 S' 的每一个事务,随后完成这一阶段。 

数据同步包含3个主要值和4种形式:

  • PeerLastZxid:Learner服务器最后处理的ZXID
  • minCommittedLog:Leader提议缓存队列中最小ZXID
  • maxCommittedLog:Leader提议缓存队列中最大ZXID

直接差异化同步 DIFF同步

如果PeerLastZxid在minCommittedLog和maxCommittedLog之间,说明Learner服务器还没有完全同步最新的数据。

  1. 首先Leader向Learner发送DIFF指令,代表开始差异化同步,然后把差异数据(从PeerLastZxid到maxCommittedLog之间的数据)提议proposal发送给Learner
  2. 发送完成之后发送一个NEWLEADER命令给Learner,同时Learner返回ACK表示已经完成了同步
  3. 接着等待集群中过半的Learner响应了ACK之后,就发送一个UPTODATE命令,Learner返回ACK,同步流程结束

先回滚再差异化同步 TRUNC+DIFF同步

Leader刚生成一个proposal,还没有来得及发送出去就宕机了,重新选举之后作为Follower,但是新的Leader没有这个proposal数据。

这个时候follower多出来的数据要先进行回滚,回滚到和新的leader最接近的PeerLastZxid的事务,leader会先发送一个TRUNC命令,然后再执行差异化DIFF同步。

仅trunc同步

如果follower的PeerLastZxid大于maxCommittedLog的场景,新的leader会仅仅发送trunc命令,将follower的数据回滚到与leader一致;

全量同步 SNAP同步

适用于两个场景:

  1. PeerLastZxid小于minCommittedLog
  2. Leader服务器上没有提议缓存队列,并且PeerLastZxid不等于Leader的最大ZXID

这两种场景下,Leader将会发送SNAP命令,把全量的数据都发送给Learner进行同步。

5.3 消息广播阶段

消息广播流程:leader收到客户端的数据后,通知所有follower;follower收到事务处理消息后,保存事务记录到history中,并且返回响应给leader,告知已收到写事务;leader收到大多数follower的响应后,就会发起提交,通知所有follower去提交事务。然后重复上述流程,直到leader出现异常。

  1. 第一阶段 Propose:Leader 收到来自客户端新的事务请求后,会生成对应的事务 Proposal,并根据 zxid 的顺序(递增)向追随自己的所有 Follower 发送 P>。
  2. 第二阶段 Ack:Follower 根据收到消息的次序来处理这些 Proposal,并追加到 H 中去,然后反馈给 Leader。
  3. 第三阶段 Commit:一旦 Follower 收到来自 Leader 的 Commit(epoch, )消息,将调用 abdeliver() 提交事务 。需要注意的是,此时 Follower 必定提交了 z' < z 之前的事务。

在进入消息广播阶段后,Leader 会为每一个 Follower 分配一个 FIFO 形式的队列进行通信,确保了同一时刻一个 Follower 只能和一个 Leader 保持同步,Leader 和 Follower 彼此之间通过心跳检测来感知。这个过程会一直持续,直到

  1. Leader 掉线了,Follower 的心跳包会超时,然后 Follower 进入 Looking 状态。
  2. Leader 没有过半的 Follower 追随了,Leader 自己进入 Looking 状态,追随它的 Follower 也会转为 Looking 状态。
6. zk有可能会出现数据不一致吗?

zk可能出现数据不一致情况,分为3个场景来描述:

查询不一致

Zk的提案是过半成功即代表成功,如果查询的节点刚好不在过半范围内,查询出来的数据就会不一致。解决方案可以在读取前使用sync命令。

leader未发送proposal宕机

如上门数据同步所说,这个时候宕机leader的提案将会被抛弃;

leader发送proposal成功,发送commit前宕机

发送proposal成功了,但将要发送commit命令前宕机了,如果重新进行选举,还是会选择zxid最大的节点作为leader,这个时候的提案不会被抛弃,新leader选举后会被重新同步到其他节点。

7. zk的脑裂问题

所谓的脑裂问题,就是在同一个集群环境,存在多个leader。ZK为了避免脑裂的问题,给出了一个规定:集群中存活的节点数必须要超过总节点数的半数才能继续提供服务,而集群服务器数量是基数台。

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/langs/790076.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-05-05
下一篇 2022-05-05

发表评论

登录后才能评论

评论列表(0条)

保存