Java | 详解ZooKeeper

本文深入介绍了ZooKeeper的起源、工作原理及其在分布式系统中的应用。涵盖了ZooKeeper的设计目标、集群角色、ZAB协议等内容,并列举了几种典型应用场景。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

ZooKeeper 起源于雅虎研究院的一个研究小组。是chubby的开源实现。设计的目标是将那些复杂而容易出错的分布式一致性服务封装起来,构成一个高效可靠的原语集,并以一系列简单易用的接口提供给用户使用。

1 概述

  • 所有读操作由 Follower 节点完成,而所有的写操作全部交由主节点完成.

  • 全量数据存储在内存中

  • 数据快照:数据快照是zk数据存储中另一个非常核心的运行机制。数据快照用来记录zk服务器上某一时刻的全量内存数据内容,并将其写入到指定的磁盘文件中,可通过dataDir配置文件目录。可配置参数snapCount,设置两次快照之间的事务操作个数,zk节点记录完事务日志时,会统计判断是否需要做数据快照(距离上次快照,事务操作次数等于snapCount/2~snapCount 中的某个值时,会触发快照生成操作,随机值是为了避免所有节点同时生成快照,导致集群影响缓慢)。

2 崩溃恢复过程

两种情况会执行选举

2.1 服务启动阶段

  • 每台服务器选举自己为leader,然后把自己的选票通过广播通知其他服务器。此时ServerState的状态是LOOKING.

  • 每台服务器接受来自其他服务器的选票的选票,并进行合法性校验,主要有两点校验,选举轮次校验和服务器状态校验。

  • 处理选票。每台服务器都会将自己的选票与其他服务器进行PK

2.2 主节点出故障

  • 各个节点向其他节点发起投票,投票当中包含自己的服务器ID及最新事务ID(ZXID),此时处于Looking状态

  • 节点会用自身的ZXID 与其他节点的ZXID做比较,如果发现其他节点的ZXID比较大,则重新发起投票(ZXID相同则取ID最大的),投票给目前已知最大的ZXID所属节点

  • 每次投票后,服务器都会统计投票数量,判断是否有某个节点得到半数以上的投票,该节点将会成为准leader, 状态变为 Leading, 其他节点为 Following

  • 发现阶段,检查是否有多个leading状态的节点,即leading接收Followler发来的各自的最新的epoch值,leading从中选出最大的epoch值,基于此值加一,生成新的epoch分发给各个Follower,各个Follower 收到最新epoch后返回ACK给leading 节点,带上各自最大的ZXID和历史事务日志

  • 同步阶段,把leader 刚才收集到的最新历史事务日志,同步给集群中所有的Follower, 只有当过半数的Follower同步成功,这个leading状态的节点才会成为正式的Leader

3 设计目标

3.1 简单的数据模型

数据树,由一系列称为ZNode的数据节点组成。

将全量数据放到内存中,以此来实现高吞吐、低延迟

3.2 可以构建集群

3.3 顺序访问

每个更新请求都会分配一个全局唯一的递增事务ID,以此编号来保证事务操作的先后顺序

3.4 高性能

全量数据存储在内存中

4 集群角色

4.1 Leader

为客户端提供读写服务

4.2 Follower

提供读服务,参与Leader选举过程,参与过半写成功策略

4.3 Observer

仅提供读服务,不参与Leader选举过程,也不参与“过半写成功”策略。可以在不影响写性能的情况下提成集群的读性能

5 ZAB协议

Zookeeper Atomic Broadcast (Zookeeper原子广播),Zookeeper 是通过 Zab 协议来保证分布式事务的最终一致性。

两种基本模式

  1. 崩溃恢复

  2. 消息广播

5.1 数据同步

Leader服务器会为每一个Follower服务器都准备一个队列,并将那些没有被Follower服务器同步的事务以Proposal消息的形式逐个发送给Follower服务器,并在每一个Proposal消息紧接着再发送一个Commit消息,以表示该事务已经被提交。

等到Follower服务器将所有其尚未同步的事务都从Leader服务器上同步过来并成功应用到本地数据库中后,Leader就会将该Follower加入到真正的可用Follower列表中

选主后,节点需要切换状态,leader切换成LEADING状态后的流程如下:

(1)重新加载本地磁盘上的数据快照至内存,并从日志文件中取出快照之后的所有事务操作,逐条应用至内存,并添加到已提交事务缓存commitedProposals。这样能保证日志文件中的事务操作,必定会应用到leader的内存数据库中。

(2)获取learner发送的FOLLOWERINFO/OBSERVERINFO信息,并与自身commitedProposals比对,确定采用哪种同步方式,不同的learner可能采用不同同步方式(DIFF同步、TRUNC+DIFF同步、SNAP同步)。这里是拿learner内存中的zxid与leader内存中的commitedProposals(min、max)比对,如果zxid介于min与max之间,但又不存在于commitedProposals中时,说明该zxid对应的事务需要TRUNC回滚;如果 zxid 介于min与max之间且存在于commitedProposals中,则leader需要将zxid+1~max 间所有事务同步给learner,这些内存缺失数据,很可能是因为leader切换过程中造成commit消息丢失,learner只完成了事务日志写入,未完成提交事务,未应用到内存。

(3)leader主动向所有learner发送同步数据消息,每个learner有自己的发送队列,互不干扰。同步结束时,leader会向learner发送NEWLEADER指令,同时learner会反馈一个ACK。当leader接收到来自learner的ACK消息后,就认为当前learner已经完成了数据同步,同时进入“过半策略”等待阶段。当leader统计到收到了一半已上的ACK时,会向所有已经完成数据同步的learner发送一个UPTODATE指令,用来通知learner集群已经完成了数据同步,可以对外服务了。

5.2 ZXID

由64位的数字组成,低32位是一个简单的单调递增计数器,高32位代表Leader周期 epoch的编号。

每当选举产生一个新的Leader就会从集群中取出最大的ZXID,并从中解析出epoch, 然后加一操作,再将低32位置0

5.3 两种模式

  • 消息广播

    • 客户端将写入数据申请发送到任意Follower
    • Follower将写入请求转发给Leader
    • Leader采用两阶段提交方式,先发送Prapose
      (带上最新的ZXID)广播给各Follower
    • Follower接收到Prapose后将该请求加入到消息历史
      队列(history queue),并返回ACK消息给Leader
    • 当leader收到大多数follower(超过法定数量)的ack消息,leader会发送commit请求
    • 当follower收到commit请求时,会判断该事务的ZXID是不是比历史队列中的任何事务的ZXID都小,如果是则提交,如果不是则等待比它更小的事务的commit
  • 崩溃恢复

    当Leader服务器出现崩溃退出或机器重启,或集群中已经不存在过半的服务器与该Leader服务器保存正常通讯,就会进入到该状态,为保存程序正确运行需要选举出一个新的Leader服务器

    • 基本特性

      • 确保那些已经在Leader服务器上提交的事务最终被所有服务器都提交

      • 确保丢弃那些只在Leader服务器上被提交的议案

        如Leader发起提案后就崩溃,则在Leader恢复后需要保证这个提案已丢弃

    • 四个阶段

      • 选主

      • 发现

      • 同步

        • 重新加载本地磁盘上的数据快照至内存,并从日志文件中取出快照之后的
          所有事务操作,逐条应用至内存,并添加到已提交事务缓存commitedProposals。
          这样能保证日志文件中的事务操作,必定会应用到leader的内存数据库中。

        • 获取learner发送的FOLLOWERINFO/OBSERVERINFO信息,并与自身commitedProposals比对,确定采用哪种同步方式,不同的learner可能采用
          不同同步方式(DIFF同步、TRUNC+DIFF同步、SNAP同步)

          • DIFF同步

            如果 zxid 介于min与max之间且存在于commitedProposals中,则leader需要将zxid+1~max 间所有事务同步给learner,这些内存缺失数据,很可能是因为leader切换过程中造成commit消息丢失,learner只完成了事务日志写入,未完成提交事务,未应用到内存

          • TRUNC+DIFF同步

            这里是拿learner内存中的zxid与leader内存中的commitedProposals(min、max)比对,如果zxid介于min与max之间,但又不存在于commitedProposals中时,说明该zxid对应的事务需要TRUNC回滚

          • SNAP同步

        • leader主动向所有learner发送同步数据消息,每个learner有自己的发送队列,互不干扰。
          同步结束时,leader会向learner发送NEWLEADER指令,同时learner会反馈一个ACK。当leader接收到来自learner的ACK消息后,就认为当前learner已经完成了数据同步,同时进入“过半策略”等待阶段。当leader统计到收到了一半已上的ACK时,会向所有已经完成数据同步的learner发送一个UPTODATE指令,用来通知learner集群已经完成了数据同步,可以对外服务了。

      • 广播

        • Client通过某个follower请求写操作时,该follower会把这个请求发给leader;
        • leader再将这个更新(proposal),顺序发送给follower;
        • follower收到leader更新时,follower会将数据持久化到磁盘;
        • 当follower写到磁盘后,就会向leader发送ACK;
        • 当leader收到半数以上的follower向它发送ACK后,就向全部的follower发送commit;
        • leader并在本地commit消息(commit的意思是就是这个消息可对外读了);
        • 当follower收到leader发送的commit后,就会将磁盘里的数据写进内存数据库,同时commit(每个follower都有内存数据库,Client去向follower请求数据时,都是通过内存数据库读取的)。同时每条消息,都有一个递增的id。

6 应用

6.1 Canal

基于mysql binlog的增量订阅和消费组件

6.2 Hadoop

zk在hadoop主要用于实现高可用

6.3 HBase

Hadoop Database 是google Bigtable的开源实现,是一个基于文件系统设计的面向海量数据的高可靠、高性能、可伸缩的分布式存储系统。

zk用作分布式协调服务

6.4 Kafka

LinkedIn 2010年12月开源的分布式消息系统。主要用于实现低延迟的发送和收集大量的事件及日志数据,是一个吞吐量极高的分布式消息系统

6.5 Dubbo

zk用作注册中心

7 ZooKeeper 定义

一个分布式的,开放源码的分布式应用程序协
调服务。为分布式应用提供一致性服务

  • 发布订阅(配置中心)
  • 负载均衡
  • 命名服务
  • 分布式协调/通知
  • 集群管理
  • Master选举
  • 分布式锁
  • 分布式队列

8 ZooKeeper 节点类型

8.1 临时节点

临时节点的生命周期和客户端会话绑定,也就是说,如果客户端会话失效,那么这个节点就会自动被清除掉

8.2 临时顺序节点

临时顺序节点其实是结合临时节点和顺序节点的特点:在某个固定的持久节点(例如/locker)下创建子节点时,zk通过将10位的序列号附加到原始名称来设置znode的路径。

8.3 持久节点

所谓持久节点,是指在节点创建后,就一直存在,直到有删除操作来主动清除这个节点,也就是说不会因为创建该节点的客户端会话失效而消失

8.5 持久顺序节点

这类节点的基本特性和上面的节点类型是一致的。额外的特性是,在ZK中,每个父节点会为他的第一级子节点维护一份时序,会记录每个子节点创建的先后顺序。基于这个特性,在创建子节点的时候,可以设置这个属性,那么在创建节点过程中,ZK会自动为给定节点名加上一个数字后缀,作为新的节点名。这个数字后缀的范围是整型的最大值。

本文完

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

孟华328

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值