ZooKeeper详解

本文深入解析ZooKeeper作为分布式协调服务的关键角色,涵盖其在分布式系统中的应用,如数据发布/订阅、命名服务、集群管理等。阐述ZooKeeper的基本概念、系统模型,以及如何通过Watcher机制和ACL策略保障数据安全。

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

zookeeper简介

分布式系统定义及面临的问题

ZooKeeper最为主要的使用场景,是作为分布式系统的分布式协同服务。我们将分布式系统定义为:分布式系统是同时跨越多个物理主机,独立运行的多个软件所组成系统。
类比一下,分布式系统就是一群人一起干活。人多力量大,每个服务器的算力是有限的,但是通过分布式系统,由n个服务器组成起来的集群,算力是可以无限扩张的。
优点显而易见,人多干活快,并且互为备份。但是缺点也很明显。我们可以想象下,以一个小研发团
队开发软件为例,假设我们有一个5人的项目组,要开始一 一个系统的开发,项目组将面临如”下问题:
在这里插入图片描述
图中列举的就是项目组将要面临到的问题,这些问题在我们日常工作中也是天天发生,并没感觉有多么
复杂,但是这是因为我们人类的大脑是个超级计算机,能够灵活应对这些问题,而且现实中信息的交换
不依赖网络,不会因网络延迟或者中断,出现信息不对等,而且现实中对以,上问题的处理其实并不严
谨,从而也引发了很多问题。想想,项目中是不是出现过沟通不畅造成任务分配有歧义?是否由于人员离职造成任务进行不下去,甚至要联系离职人员协助?是不是出现过任务分配不合理?类似这样的各种问题,肯定会发生于你的项目组中。在现实世界,我们可以人为去协调,即使出错了,人工去补错,加加班搞定就好。但在计算机的世界,这样做是行不通的,一切都要保证严谨,以上问题要做到尽可能不要发生。因此,分布式系统必须采用合理的方式解决掉以上的问题

实际上要想解决这些问题并没有那么复杂,我们仅需要做-件事就可以万事无—让信息在项目组成员同步。如果能做到信息同步,那么每个人在干什么,大家都是清楚的,干到什么程度也是清晰的,无论谁离职也不会产生问题。分配的工作,能够及时清晰的同步给每个组员,确保每个组员收到的任务分
配没有冲突。

分布式系统的协调工作就是通过某种方式,让每个节点的信息能够同步和共享。这依赖于服务进程之间
的通信。通信方式有两种:

● 通过网络进行信息共享
这就像现实中,开发leader在会上把任务传达下去,组员通过听leader命令或者看leader的邮件知道自
己要干什么。当任务分配有变化时,leader会单 独告诉组员,或者再次召开会议。信息通过人与人之间
的直接沟通,完成传递。
● 通过共享存储
这就好比开发leader按照约定的时间和路径,把任务分配表放到了svn上,组员每天去svn.上拉取最新的
任务分配表,然后干活。其中svn就是共享存储。更好一点的做法是,当svn文件 版本更新时,触发邮件通知,每个组员再去拉取最新的任务分配表。这样做更好,因为每次更新,组员都能第一时间得到消
息,从而让自己手中的任务分配表永远是最新的。此种方式依赖于中央存储。整个过程如下图所示:
在这里插入图片描述

zookeeper如何解决分布式系统面临的问题

ZooKeeper对分布式系统的协调,使用的是第二种方式,共享存储。其实共享存储,分布式应用也需要
和存储进行网络通信。
实际上,通过ZooKeeper实现分布式协同的原理,和项目组通过SVN同步工作任务的例子是-样的。
ZooKeeper就像是svn,存储了任务的分配、完成情况等共享信息。每个分布式应用的节点就是组员,
订阅这些共享信息。当主节点(组leader) ,对某个从节点的分工信息作出改变时,相关订阅的从节点
得到zookeeper的通知,取得自己最新的任务分配。完成工作后,把完成情况存储到zookeeper。主节
点订阅了该任务的完成情况信息,所以将得到zookeeper的完工的通知。参考下图,是不是和前面项目
组通过svn分配工作的例子一模一样?仅仅是把svn和邮件系统合二为一,以ZooKeeper代替
在这里插入图片描述
注: Slave节点要想获取ZooKeeper的更新通知,需事先在关心的数据节点上设置观察点。
大多数分布式系统中出现的问题,都源于信息的共享出了问题。如果各个节点间信息不能及时共享和同
步,那么就会在协作过程中产生各种问题。ZooKeeper解决协同问题的关键, 就是在于保证分布式系统信息的一-致性。

zookeeper的基本概念

Zookeeper是一个开源的分布式协调服务,其设计目标是将那些复杂的且容易出错的分布式-致性服务封装起来,构成-一个高效可靠的原语集,并以一些简单的接口提供给用户使用。
zookeeper是一个典型的分布式数据一致性的解决方案,分布式应用程序可以基于它实现诸如数据订阅/发布、负载均衡、命名服务、集群管理、分布式锁和分布式队列等功能

基本概念

①集群角色
通常在分布式系统中,构成一个集群的每一台机器都有 自己的角色,最典型的集群就是Master/Slave模式(主备模式),此情况下把所有能够处理写操作的机器称为Master机器,把所有通过异步复制方式获取最新数据,并提供读服务的机器为Slave机器。
而在Zookeeper中,这些概念被颠覆了。它没有沿用传递的Master/Slave概念,而是引入了Leader、Follower、Observer三种角 色。Zookeeper集 群中的所有机器通过Leader选举来选定一台被称为Leader的机器,Leader服 务器为客户端提供读和写服务,除Leader外,其他机器包括Follower和 Observer,Follower和Observer都能提供读服务,唯- -的区别在于Observer不参与Leader选举过程,不参与写操作的过半写成功策略,因此Observer 可以在不影响写性能的情况下提升集群的性能。
在这里插入图片描述
过半成功策略:什么是过半成功策略。写入过程由leader节点处理。leader将写入请求转发到所有节点,并等待节点的回复。如果一半以上的节点回复,则写入过程完成。

②会话(session)
Session指客户端会话,-一个客户端连接是指客户端和服务端之间的一个TCP长连接,Zookeeper对外的服务端口默认为2181,客户端启动的时候,首先会与服务器建立一个TCP连接,从第-次连接建立开始,客户端会话的生命周期也开始了,通过这个连接,客户端能够心跳检测与服务器保持有效的会话,也能够向Zookeeper服务器发送请求并接受响应,同时还能够通过该连接接受来自服务器的Watch事件通知。

③数据节点(Znode)
在谈到分布式的时候,我们通常说的“节点”是指组成集群的每一台机器。 然而,在ZooKeeper中, “节点”分为两类,第一类同样是指构成集群的机器,我们称之为机器节点;第二类则是指数据模型中的数据单元,我们称之为数据节点-- ZNode。 ZooKeeper将所有数据存储在内存中,数据模型是-棵树(ZNode Tree),由斜杠(/) 进行分割的路径,就是一个Znode,例如/app/path1。 每个ZNode.上都会保存自己的数据内容,同时还会保存-系列属性信息。

④版本
刚刚我们提到,Zookeeper的每 个Znode.上都会存储数据,对于每个ZNode, Zookeeper都会 为其维护-个叫作Stat的数据结构,Stat记录 了这个ZNode的三个数据版本,分别是version ( 当前ZNode的版本)、cversion (当前ZNode子 节点的版本)、 aversion (当 前ZNode的ACL版本)。

⑤Watcher (事件监听器)
Wathcer (事件监听器),是Zookeeper中一个很重 要的特性,Zookeeper允许用户在指定节点 上注册一些Watcher,并且在一些特定事件触发的时Zookeeper服务 端会将事件通知到感兴趣的客户端,该机制是Zookeeper实现分布式协调服务的重要特性

⑥ACL
Zookeeper采用ACL (Access Control Lists)策略来进行权限控制,其定义了如下五种权限:
CREATE:创建子节点的权限。
READ:获取节点数据和子节点列表的权限。
WRITE:更新节点数据的权限。
DELETE:删除子节点的权限。
ADMIN:设置节点ACL的权限。

其中需要注意的是,CREATE和DELETE这 两种权限都是针对子节点的权限控制

zookeeper系统模型

ZooKeeper数据模型Znode
在ZooKeeper中,数据信息被保存在一个个数据节点. 上,这些节点被称为znode。ZNode 是Zookeeper中最小数据单位,在ZNode’下面又可以再挂ZNode,这样-层层下去就形成了一个层次化命名空间ZNode树,我们称为ZNode Tree,它采用了类似文件系统的层级树状结构进行管理。见下图示例:
在这里插入图片描述
在Zookeeper中,每一个数据节点都是一个 ZNode,.上图根目录下有两个节点,分别是: app1和app2,其中app1下面又有三个子节点,所有ZNode按层次化进行组织,形成这么一颗树,ZNode的节点路径标识方式和Unix文件系统路径非常相似,都是由- -系列使用斜杠(1) 进行分割的路径表示,开发人员可以向这个节点写入数据,也可以在这个节点下面创建子节点。

ZNode的类型

刚刚已经了解到,Zookeeper的znode tree是由-系列数据节点组成的,那接下来,我们就对数据节点做详细讲解

Zookeeper节点类型可以分为三大类:
持久性节点(Persistent)
临时性节点(Ephemeral)
顺序性节点(Sequential)

在开发中在创建节点的时候通过组合可以生成以下四种节点类型:持久节点、持久顺序节点、临时节点、临时顺序节点。不同类型的节点则会有不同的生命周期

持久节点:是Zookeeper中最常见的一种节点类型,所谓持久节点,就是指节点被创建后会- -直存在服务器,直到删除操作主动清除
持久顺序节点:就是有顺序的持久节点,节点特性和持久节点是一样的,只是额外特性表现在顺序上。顺序特性实质是在创建节点的时候,会在节点名后面加上一个数字后缀,来表示其顺序。
临时节点:就是会被自动清理掉的节点,它的生命周期和客户端会话绑在一起,客户端会话结束,节点会被删除掉。与持久性节点不同的是,临时节点不能创建子节点。
临时顺序节点:就是有顺序的临时节点,和持久顺序节点相同,在其创建的时候会在名字后面加上数字后缀。

事务ID
首先,先了解,事务是对物理和抽象的应用状态.上的操作集合。往往在现在的概念中,狭义上的事务通常指的是数据库事务,-般包含了一系列对数据库有序的读写操作,这些数据库事务具有所谓的ACID特性,即原子性(Atomic) 、一致性(Consistency) 、 隔离性(lsolation) 和持久性(Durability) 。
而在ZooKeeper中,事务是指能够改变ZooKeeper服务器状态的操作,我们也称之为事务操作或更新操作,一般包括数据节点创建与删除、数据节点内容更新等操作。对于每一个事务请求,ZooKeeper都会为其分配一一个全局唯一的事务ID,用ZXID来表示,通常是一个64位的数字。每一个ZXID对应一次更新操作,从这些ZXID中可以间接地识别出ZooKeeper处理这些更新操作请求的全局顺序

ZNode的状态信息

在这里插入图片描述
整个ZNode节点内容包括两部分:节点数据内容和节点状态信息。图中quota 是数据内容,其他的属于状态信息。那么这些状态信息都有什么含义呢?

cZxid就是Create ZXID, 表示节点被创建时的事务ID。
ctime就是Create Time, 表示节点创建时间。
mZxid就是Modified ZXID,表示节点最后一次被修改时的事务ID。
mtime就是Modified Time,表示节点最后一次被修改的时间。
pZxid表示该节点的子节点列表最后一次被修改时的事务ID。 只有子节点列表变更才会更新pZxid,子节点内容变更不会更新。
cversion 表示子节点的版本号。
dataVersion表示内容版本号。
aclversion标识ac1版本
ephemeralOwner表示创建该临时节点时的会话sessionID, 如果是持久性节点那么值为0
dataLength表示数据长度。
numChildren表示直系子节点数。

Watcher数据变更通知

Zookeeper使用Watcher机制实现分布式数据的发布/订阅功能
一个典型的发布/订阅模型系统定义了一种一对多的订阅关系,能够让多个订阅者同时监听某一个主题对象,当这个主题对象自身状态变化时,会通知所有订阅者,使它们能够做出相应的处理。
在ZooKeeper中,引入了Watcher 机制来实现这种分布式的通知功能。ZooKeeper 允许客户端向服务端注册一个Watcher监听,当服务端的一-些指定事件触发了这个Watcher,那么就会向指定客户端发送一个事件通知来实现分布式的通知功能。
整个Watcher注册与通知过程如图所示。
在这里插入图片描述

Zookeeper的Watcher机制主要包括客户端线程客户端WatcherManagerZookeeper服务器三部分。
具体工作流程为:客户端在向Zookeeper服务器注册的同时,会将Watcher对象存储在客户端的WatcherManager当中。当Zookeeper服务器触发Watcher事件后,会向客户端发送通知,客户端线程从WatcherManager中取出对应的Watcher对象来执行回调逻辑。

ACL保障数据的安全

Zookeeper作为一个分布式协调框架,其内部存储了分布式系统运行时状态的元数据,这些元数据会直接影响基于Zookeeper进行构造的分布式系统的运行状态,因此,如何保障系统中数据的安全,从而避免因误操作所带来的数据随意变更而导致的数据库异常十分重要,在Zookeeper中, 提供了一套完善的
ACL (Access Control List)权限控制机制来保障数据的安全。
我们可以从三个方面来理解ACL机制:权限模式(Scheme)授权对象 (ID)权限(Permission),通常使用"scheme: id : permission"来标识一个有效的ACL信息。

权限模式: Scheme
权限模式用来确定权限验证过程中使用的检验策略,有如下四种模式:

  1. IP
    IP模式就是通过IP地址粒度来进行权限控制,如"ip:192.168.0.1 10"表示权限控制针对该IP地址,同时IP模式可以支持按照网段方式进行配置,如"ip:192.1 68.0.1/24"表示针对192.168.0.*这个网段进行权限控制。
  2. Digest
    Digest是最常用的权限控制模式,要更符合我们对权限控制的认识,其使用"username:password"形式的权限标识来进行权限配置,便于区分不同应用来进行权限控制。当我们通过"username:password"形式配置了权限标识后,Zookeeper会先后对其进行SHA-1加密.和BASE64编码
  3. World
    World是一种最开放的权限控制模式,这种权限控制方式几乎没有任何作用,数据节点的访问权限对所有用户开放,即所有用户都可以在不进行任何权限校验的情况下操作ZooKeeper.上的数据。另外,World模 式也可以看作是一种特殊的Dgest模式,它只有一个权限标识,即"world: anyone"。
  4. Super
    Super模式,顾名思义就是超级用户的意思,也是一种特殊的Digest模式。在Super模式下,超级用户可以对任意ZooKeeper.上的数据节点进行任何操作。

授权对象: ID
授权对象指的是权限赋予的用户或一个指定实体,例如IP地址或是机器等。在不同的权限模式下,授权对象是不同的,表中列出了各个权限模式和授权对象之间的对应关系。
在这里插入图片描述
权限
权限就是指那些通过权限检查后可以被允许执行的操作。在ZooKeeper中,所有对数据的操作权限分为以下五大类:
CREATE © :数据节点的创建权限,允许授权对象在该数据节点下创建子节点
DELETE (D) :子节点的删除权限,允许授权对象删除该数据节点的子节点
READ ® :数据节点的读取权限,允许授权对象访问该数据节点并读取其数据内容或子节点列表等。
WRITE (W) : 数据节点的更新权限,允许授权对象对该数据节点进行更新操作。
ADMIN (A) :数据节点的管理权限,允许授权对象对该数据节点进行ACL相关的设置操作。

Zookeeper应用场景

ZooKeeper是一个典型的发布/订阅模式的分布式数据管理与协调框架,我们可以使用它来进行分布式数据的发布与订阅。另一方面,通过对ZooKeeper中丰富的数据节点类型进行交叉使用,配合Watcher事件通知机制,可以非常方便地构建一系列分布式应用中都会涉及的核心功能,如数据发布/订阅、命名
服务、集群管理、Master选举、分布式锁和分布式队列等。那接下来就针对这些典型的分布式应用场景来做下介绍

数据发布/订阅

数据发布/订阅(Publish/Subscribe) 系统,即所谓的配置中心,顾名思义就是发布者将数据发布到ZooKeeper的一个或一系列节点上,供订阅者进行数据订阅,进而达到动态获取数据的目的,实现配置信息的集中式管理和数据的动态更新。
发布/订阅系统-般有两种设计模式,分别是推(Push) 模式和拉(Pull) 模式。在推模式中,服务端主动将数据更新发送给所有订阅的客户端;而拉模式则是由客户端主动发起请求来获取最新数据,通常客户端都采用定时进行轮询拉取的方式。
ZooKeeper采用的是推拉相结合的方式:客户端向服务端注册自己需要关注的节点,一旦该节点的数据发生变更,那么服务端就会向相应的客户端发送Watcher事件通知,客户端接收到这个消息通知之后,需要主动到服务端获取最新的数据。
如果将配置信息存放到ZooKeeper上进行集中管理,那么通常情况下,应用在启动的时候都会主动到ZooKeeper服务端上进行一次配置信息的获取,同时,在指定节点上注册一个Watcher监听,这样一来,但凡配置信息发生变更,服务端都会实时通知到所有订阅的客户端,从而达到实时获取最新配置信息的目的。

下面我们通过一个"配置管理”的实际案例来展示ZooKeeper在"数据发布/订阅”场景下的使用方式。

在我们平常的应用系统开发中,经常会碰到这样的需求:系统中需要使用- -些通用的配置信息,例如机器列表信息、运行时的开关配置、数据库配置信息等。这些全局配置信息通常具备以下3个特性。
●数据量通常比较小。
●数据内容在运行时会发生动态变化。
●集群中各机器共享,配置一致。

对于这类配置信息,一般的做法通常可以选择将其存储在本地配置文件或是内存变量中。无论采用哪种方式,实都可以简单地实现配置管理,在集群机器规模不大、配置变更不是特别频繁的情况下,无论刚刚提到的哪种方式,都能够非常方便地解决配置管理的问题。但是,一旦机器规模变大,且配置信息变更越来越频繁后,我们发现依靠现有的这两种方式解决配置管理就变得越来越困难了。我们既希望能够快速地做到全局配置信息的变更,同时希望变更成本足够小,因此我们必须寻求-种更为分布式化的解决方案

接下来我们就以一个”数据库切换”的应用场景展开,看看如何使用ZooKeeper来实现配置管理:
●配置存储
在进行配置管理之前,首先我们需要将初始化配置信息存储到Zookeeper上去,一般情况下,我们可以在Zookeeper.上选取一个数据节点用于配置信息的存储,例如: /app1/database_ config
在这里插入图片描述配置管理的zookeeper节点示意图

我们将需要管理的配置信息写入到该数据节点中去,例如:

#数据库配置信息
#DBCP
dbcp.driverClassName=com.mysql.jdbc.Driver
dbcp.dbJDBCUrl=jdbc:mysql://127.0.0.1:3306/test
dbcp.username=zm
dbcp.password=1234
dbcp.maxActive=30
dbcp.maxIdle=10

配置获取
集群中每台机器在启动初始化阶段,首先会从上面提到的ZooKeeper配置节点上读取数据库信息,同时,客户端还需要在该配置节点上注册一个数据变更的Watcher监听,一旦发生节点数据变更,所有订阅的客户端都能够获取到数据变更通知。

配置变更
在系统运行过程中,可能会出现需要进行数据库切换的情况,这个时候就需要进行配置变更。借助ZooKeeper,我们只需要对ZooKeeper上配置节点的内容进行更新,ZooKeeper就能够 帮我们将数据变更的通知发送到各个客户端,每个客户端在接收到这个变更通知后,就可以重新进行最新数据的获取。

命名服务

命名服务(Name Service)也是分布式系统中比较常见的一类场景,是分布式系统最基本的公共服务之一。在分布式系统中,被命名的实体通常可以是集群中的机器、提供的服务地址或远程对象等一- 这些我们都可以统称它们为名字(Name) ,其中较为常见的就是一些分布式服务框架(如RPC、 RMI)中的服务地址列表,通过使用命名服务,客户端应用能够根据指定名字来获取资源的实体、服务地址和提供者的信息等。

ZooKeeper提供的命名服务功能能够帮助应用系统通过一个资源引用的方式来实现对资源的定位与使用。另外,广义上命名服务的资源定位都不是真正意义的实体资源一-在分 布式环境中,上层 应用仅仅需要一个全局唯- -的名字,类似于数据库中的唯一主键。

所以接下来。我们来看看如何使用ZooKeeper来实现-套分布式全局唯- -ID的分配机制

所谓ID,就是一个能够唯一标识某个对象的标识符。在我们熟悉的关系型数据库中,各个表都需要一个主键来唯一标识每条数据库记录, 这个主键就是这样的唯- -ID。 在过去的单库单表型系统中,通常可以使用数据库字段自带的auto. increment属性来自动为每条数据库记录生成-个唯- -的ID,数据库会保证生成的这个ID在全局唯一。 但是随着数据库数据规模的不断增大,分库分表随之出现,而auto increment属性仅能针对单一表中的记录自动生成ID,因此在这种情况下,就无法再依靠数据库的auto_ increment属性来唯一标识一 条记录了。于是,我们必须寻求- -种能够在分布式环境下生成全局唯一ID的方法。

一说起全局唯一ID, 相信大家都会联想到UUID。没错,UUID是通用唯一识别码(UniversallyUnique ldentifier)的简称, 是一种在分布式系统中广泛使用的用于唯- -标识元素的标准确实, UUID是一个非常不错的全局唯- -ID生成方式, 能够非常简便地保证分布式环境中的唯一-性。

一个标准的UUID是一个包含32位字符和4个短线的字符串,例如"e70f1357-f260-46ff-a32d-53a086c57ade"。UUID的优势自然不必多说,我们重点来看看它的缺陷。

长度过长
UUID最大的问题就在于生成的字符串过长。显然,和数据库中的INT类型相比,存储一个UUID需要花费更多的空间。
含义不明
上面我们已经看到一个典型的UUID是类似于"e70f1357-f260-46ff-a32d-53a086c57ade"的一一个字符串。根据这个字符串,开发人员从字面上基本看不出任何其表达的含义,这将会大大影响问题排查和开发调试的效率。

所以接下来,我们结合-一个分布式任务调度系统来看看如何使用ZooKeepe来实现这类全局唯- -ID的生成。之前我们已经提到,通过调用ZooKeeper节点创建的API接口可以创建-个顺序节点,并且在API返回值中会返回这个节点的完整名字。利用这个特性,我们就可以借助ZooKeeper来生成全局唯-的ID了,如下图:
在这里插入图片描述

说明,对于一个任务列表的主键,使用ZooKeeper生 成唯一-ID的基 本步骤:
1.所有客户端都会根据自己的任务类型,在指定类型的任务下面通过调用create ()接口来创建一个顺序节点,例如创建"job-”节点。
2.节点创建完毕后,create ()接口会返回一个完整的节点名,例如"ob-0000000003" 。
3.客户端拿到这个返回值后,拼接上type类型,例如"type2-j0-000000003”", 这就可以作为一个全局唯一的ID了。

在ZooKeeper中,每一个数据节点都能够维护-份子节点的顺序顺列,当客户端对其创建-个顺序子节点的时候ZooKeeper会自动以后缀的形式在其子节点上添加一个序号,在这个场景中就是利用了ZooKeeper的这个特性

集群管理

Master选举

分布式锁

排它锁

共享锁

分布式队列

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值