关注wx:CodingTechWork,一起学习进步。
引言
对zk的学习和简单实用进行一个总结。
zk介绍
zk概述
- zk是一个具有高可用性的高性能协调服务。
- zk的watcher对象有两个作用:一方面是用于获得zk状态变化的通知;另一方面是用于获得znode变化的相关通知。
zk特点
- zk是简单的,核心是一个精简的文件系统,提供诸如排序和通知等简单的操作和额外的抽象操作。
- zk是富有表现力的,zk的基本操作是一组丰富的构件,可用于实现多种协调数据结构和协议,如分布式队列、分布式锁和一组节点的“领导者选举”。
- zk具有高可用性,可以帮助系统避免出现单点故障,从而构件一个可靠的应用程序。
- zk采用松耦合交互方式,交互过程中,参与者可不需要彼此了解,如zk可被用于实现“数据汇集”机制,让进程在不了解其他进程(或网络)情况下能够彼此发现并进行信息交互。一个进程可以在zk中留下一条消息后关闭,另外一个进程还可以继续读取这条消息。
- zk是一个资源库,提供一个通用协调模式实现方法的开源共享库。
- zk是高性能的,基准吞吐量超过每秒10000个操作。
zk数据模型
数据模型特点
- 树形节点:zk维护一个
树形层次结构
,树中的节点被称为znode
。znode可以用于存储数据
,并且有一个与之相关联的ACL
。 - 小数据存储:zk被设计用来实现协调服务,通常使用小数据文件,而不是用于大容量数据存储,一个znode能存储的数据被限制在
1MB
以内。 - 原子性数据访问:zk的数据访问
具有原子性
。客户端读取一个znode的数据时,要么读到所有数据,要么读操作全部失败(不会存在读取部分数据这种状态)。同样,写操作也是一样,要么全部调换znode存储数据,要么写不成功而失败,不会出现部分写现象。 - 路径引用:znode通过路径被引用,路径用斜杠分割的Unicode字符串,
路径必须是绝对路径
,每条路径从一个斜杠字符开始
。路径引用中不包含“.”这种不合法表示
,不支持路径解析。hadoop是通过URI,如hdfs://ns1/user/userA - "zookeeper"字符串是一个
保留词
,不能作为路径表示中的一部分。zk中使用/zookeeper子树
保存管理信息
。
znode
znode概念
- zk可以看作是一个具有高可用性特征的文件系统,没有文件和目录,而是使用“节点”概念,称为znode,znode既可以作为保存数据的容器(类似于文件),也可以作为保存其他znode的容器(类似于目录)
-
所有znode构成一个层次化的命名空间,创建一个以组名为节点名的znode作为父节点,然后以组成员名(服务器名)为节点名来创建作为子节点的znode。
znode分类
znode分为短暂和持久两种大类型。类型在创建时被确定且后面不能再修改。
短暂znode
- 在创建短暂znode的客户端会话结束时,zk会将该短暂的znode删除。在创建持久znode时,不依赖于客户端会话,只有客户端明确要删除znode时,才会被删除。
- 短暂znode不可以有子节点。
- 短暂znode都会被板顶到一个客户端会话,但对所有客户端可见。
- 短暂znode应用:使用短暂znode来实现一个组成员管理服务,让任何进程都知道在特定的时刻有哪些组成员可用。
顺序znode
- 顺序znode是指名称中包含zk指定顺序号的znode,若在创建znode时设置了顺序标识,则该znode名称之后会附加一个值,这个值是由一个单调递增的计数器(由父节点维护)所添加的。
观察机制
- 观察机制通知客户端znode的变化。
- 可以针对zk服务的操作设置观察,如客户端A可以对一个znode调用exists操作,设定一个观察,若znode不存在,客户端A调用exists操作返回false。过段时间后,若另外一个客户端B创建了这个znode,观察被触发,通知前一个客户端A这个znode被创建。
- 观察只能被触发一次,若想多次收到观察通知,客户端需要重注册所需要的观察。
zk实现
zk运行模式分类
- 独立模式:只有一个zk服务器,简单,适合测试环境,不能保证高可用性和可恢复性。
- 复制模式:
奇数个
服务器,运行一个计算机集群上,这个计算机集群被称为一个集合体,zk通过复制来实现高可用性,只要集合体中半数以上的机器处于可用状态,就能够提供服务。对znode树的每一个修改都会被复制到集合体中超过半数的机器上。
zk服务器为何是奇数个?
因为zk生产环境一般是使用复制模式,即集合体中半数以上的机器处于可用状态,zk就可以继续提供服务,比如在一个有5个节点的集合体中,任意2台机器故障,都可以保证服务继续。而在6个节点的集合体里,只能够容忍2台机器出现故障,如果出现3台机器,剩下3台机器没有超过集合体的半数,无法判断是否可用。
zk实现原理
zk使用了Zab协议
,经历领导者选举
和原子广播
两个循环重复的阶段。
- 领导者选举:集合体中的所有机器通过一个选择过程选出一台被称为
领导者(leader)
的机器,其他机器称为跟随者(follower)
,一旦半数以上的跟随者将其状态与领导者同步了,则表明该阶段完成。 - 原子广播:所有的写请求都会被转发给领导者,再由领导者更新广播给跟随者,当半数以上的跟随者已经将修改持久化后,领导者才会提交这个更新,客户端会收到一个更新成功的响应。
如果领导者机器出现故障,则剩余机器会选出一个新的领导者机器。若旧领导者恢复正常,会变成一个跟随者。一般选举只需要200毫秒
。
zk数据一致性
客户端连接到哪一台机器?
一个跟随者有可能滞后于领导者几个更新,而每个客户端都有可能被连接到领导者,但客户端对此无法控制,无法知道自己是否连接到领导者机器。所以客户端最好是全部连接到领导者机器,若实现不了,最好不要连接到领导者机器。对zk进行配置,使得领导者不接受任何客户端连接,领导者任务就变成了协调更新,可以将leaderServes属性设置为no来实现,推荐操作3台服务器的集群中使用该配置。
zxid
- 每一个对znode树的更新都被赋予一个全局唯一的ID,即为
zxid(ZooKeeper Transaction ID)
。 - zk要求对所有的更新进行编号排序,决定了分布式系统的执行顺序。
数据一致性的保证
- 顺序一致性:来自任意特定客户端的更新都会按照其发送顺序被提交。如znode a值改为b,然后b值改为c,则其他所有客户端看到c后,都不会看到b值。(如果没有客户端对a值更新)执行是有顺序的。
- 原子性:每个更新要么成功,要么失败。若一个更新失败,不会有客户端看到这个更新的结果。
- 单一系统映像:一个客户端无论连哪台机器,都看到同样的系统视图。若一个客户端在同一个会话中连接到一台新服务器,所看到的系统状态不会比上一个服务器上所看到的更老。若一台服务器A出现故障,它的客户端尝试连接其他服务器时,所有状态滞后于服务器A的都不会接受该连接请求,而是去连接状态超前或同于服务器A的机器。
- 持久性:一个更新一旦成功,其结果就会持久存在且不会被撤销,所以更新操作不会受到服务器故障的影响。
- 及时性:任何客户端的滞后性有限,一般是不超过几十秒。由于性能要求,所有的读操作都是从zk服务器的内存中读数据,不参与写操作的全局排序。
ZK会话
会话介绍
- 每个zk客户端的配置中都包含集合体服务器列表,在启动时,客户端会尝试连接列表中的一台服务器,若连接失败,会尝试连接列表中其他服务器,一直练到成功或者全部连不上而失败。
- 客户端一旦和zk服务器建立连接,这台服务器就会为该客户端创建一个新的会话,每个会话都有一个超时时间的设置,由创建会话的应用来设定。若服务器在超时时间内没有收到任何请求,相应的会话会过期。一旦过期,则无法重新打开。
- 一个会话空闲超过一段时间,可以通过客户端发送ping请求来保持会话不过期(ping请求由zk的客户端库自动发送),这个时间足够低,监测出服务器故障,zk客户端会自动进行故障切换,切换到另一台服务器,会话有效。切换过程中的客户端其他操作会失败。
时间
-
“滴答时间”
:tick time定义了zk的基本时间周期,被集合体的服务器用来定义相互交互的时间表。 - 会话超时值设置基于滴答时间,大于等于2个滴答时间,小于等于20个滴答时间。如通常滴答参数设为
2秒(2000毫秒)
,则会话超时
时间为4-40秒
。 - 会话超时时间若设置太短,会较快监测到机器故障,但是要避免
设置太低
,繁忙的网络会导致数据包传输延迟,导致会话过期,机器会出现“振动(flap)”现象
,即在很短的时间内反复出现离开后又重新加入组的情况。 - 会话超时时间设置大一点时,适用于重建会话代价较大的应用程序客户端,比如对应用进行维护或升级时,超时时间大,重启应用程序,也可以避免会话过期(重启时,未检测会话是否过期,避免过期),服务器会为每个会话分配一个唯一的ID和密码,建立连接过程中传递给zk,可以用于恢复一个会话。
- 一般zk的集合体服务器越多,会话超时的设置越大。可使用jmx监控zk度量指标,频繁遇到丢连接时,要考虑增大超时的设置。
状态
zk对象在生命周期中可以通过
getState()
方法查询对象的状态,返回值States是zk对象不同状态的枚举类型值。
- 建立连接过程中,zk实例处于
CONNECTING状态
,建立连接完成后,进入CONNECTED状态
。通过注册观察对象,进入CONNECTED后,观察对象会收到一个WatchedEvent通知
,KeeperState值为SyncConnected
。 - zk实例在断开然后重连zk服务,状态就会在CONNECTED和CONNECTING之间转换。若断开连接会收到一个
Disconnected事件
。(重连是zk自动发起) - 若调用
close()
方法或者出现会话超时(观察事件KeeperState的值为Expired
时),zk实例会转换到第三个状态CLOSED
。zk对象不再被认为是活跃的(通过State使用isAlive()方法判断),且不能再用。
zk应用
配置服务
配置服务是分布式应用中的基本服务之一,使集群中的机器可以共享配置信息中公共部分。zk可以作为一个具有高可用性的配置存储器,允许分布式应用的参与者检索和更新配置文件。
- 存储的配置数据是字符串,关键字是znode路径,每个znode上存储了一个键值对;
- 在任何时刻只有一个客户端执行更新操作,其他客户端负责观察。
锁服务
分布式锁能够在一组进程之间提供互斥机制,使任何时刻只有一个进程可以持有锁。可以用于在大型分布式系统中实现领导者选举,在任何时间点,持有锁的那个进程就是系统的领导者。(不同于zk自身的领导者选举)
实现原理
使用顺序znode来为竞争锁的进程强制排序,zk是顺序的仲裁者,负责分配顺序号。
- 首先制定一个作为锁的znode,通常用它来描述被锁定的实体,称为/leader。
- 然后希望获得锁的客户端创建一些短暂顺序znode,作为锁znode的子节点。在任何时间点,顺序号最小的客户端持有锁。如2个客户端几乎同时创建znode,为/leader/lock-1和/leader/lock-2,则创建/leader/lock-1的客户端持有锁。若删除znode /leader/lock-1,则释放锁,创建/leader/lock-2的客户端会持有锁。
zk安装
安装流程
- 下载
进入官网:https://zookeeper.apache.org/,点击【Project】—>【Releases】寻找合适的版本。 - 解压
tar xzf zookeeper-x.y.z.tar.gz
- 加入命令行路径
export ZOOKEEPER_HOME=~/sw/zookeeper-x.y.z
export PATH=$PATH:%ZOOKEEPER_HOME/bin
- 配置文件(主要)
配置 | 说明 |
---|---|
zoo.cfg | 一般放在conf子目录或者/etc/zookeeper子目录中,若设置环境变量ZOOCFGDIR,可以保存在该环境变量所指定的目录 |
tickTime=2000 | 指定zk基本事件单元(毫秒) |
dataDir=/Users/tom/zookeeper | dataDir指定zk存储持久数据的本地系统位置 |
clientPort=2181 | 指定zk用于监听客户端连接的端口 |
- 启动
zkServer.sh start
zk配置服务器ID
-
zk服务器的集合体中,每个服务器都有一个数值型的ID,服务器ID在集合体中唯一,取值为1~255,通过一个名为myid的纯文本文件设定服务器的ID,保存在dataDir参数所指定的目录中。
- 为每台服务器设置ID,还需要将集合体中其他服务器ID和网络位置告诉所有服务器,通过以下方式设置:
server.n=hostname:port:port
其中,n是服务器的ID,第一个端口是跟随者用来连接领导者的端口,第二个端口用于领导者选举。举例:
a)服务器在3个端口上进行监听:2888端口用于客户端连接;
b)对于领导者而言,2888端口被用于跟随者连接;
c)3888端口被用于领导者选举阶段的其他服务器连接。 - 流程:当一个zk服务器启动时,它读取myid文件用于确定自己服务器ID;然后,通过读取配置文件来确定应当在哪个端口进行监听;同时,确定集合体中其他服务器的网络地址。在客户端中,其实就是通过zookeeper1:2181、zookeeper2:2181和zookeeper3:2181作为主机字符串。
zk配置时间参数
initLimit和syncLimit参数是强制的,都是以滴答参数(tickTime)的倍数进行度量。
- initLimit参数:设定所有跟随者与领导者进行连接并同步的时间范围。在设定的时间段内,半数以上的跟随者若未能完成同步,领导者便会放弃领导地位,进行另外一个领导者的选举。若经常发生这种情况,需要调大参数值。
- syncLimit参数:设定允许一个跟随者与领导者进行同步的时间。在设定的时间段内,一个跟随者未能完成同步,会自己重启。所有关联到跟随者的客户端则会连接到另外一个跟随者
zk操作
服务操作
操作 | 描述 |
---|---|
create | 创建一个znode(必须要有父节点) |
delete | 删除一个znode(不能有任何子节点) |
exists | 测试一个znode存在与否并且查询它的元数据 |
getACL、setACL | 获取/设置一个znode的ACL |
getChildren | 获取一个znode的子节点列表 |
getData、setData | 获取/设置一个znode所保存的数据 |
sync | 将客户端的znode视图与zk同步 |
观察事件类型
查看zk的连接数
- 查看zk的连接数的几种命令
netstat -na | grep 2181 | wc -l
netstat -an | grep -I 2181
echo stat | nc localhost 2181
- 查看最大连接数配置
echo conf | nc localhost 2181 | grep "max"
查看znode节点列表
- 查看命令行帮助
./zkCli.sh -server localhost ls
- 查看所有组
./zkCli.sh -server localhost ls /
- 查看具体组
./zkCli.sh -server localhost ls /brokers
常用操作命令
操作 | 描述 | |
---|---|---|
get path [watch] | 1. 获取指定节点信息(包括数据内容和节点状态信息); 2. 节点路径必须以 / 开头;3. watch为可选参数,表示是否注册监听,若添加该参数,则其他客户端修改节点数据后,当前客户端可收到数据变更通知; 4. 示例: get /znode 或get /znode watch |
|
stat path [watch] | 用于查看节点状态信息,与get命令类似。如stat / |
|
set path data [version] | 对指定znode添加内容,version表示可以指定的dataVersion | |
ls path [watch] | 获取当前路径下的子节点列表(仅仅包含下一级子节点) | |
ls2 path [watch] | ls的增强版,获取当前路径下的子节点列表(仅仅包含下一级子节点)以及当前节点下的状态信息 | |
delete path [version] | 删除指定节点,当指定节点下有子节点时,该节点无法删除 | |
rmr path | 增强版delete,可以递归删除指定节点 | |
create [-s] [-e] path data acl | 创建节点,-s表示顺序节点,-e表示临时节点,acl表示设置权限控制 | |
sync path | 强制同步,由于请求在半数以上的服务器上生效就表示此请求生效,name就会有一些服务器数据是旧的,sync可以强制这些服务器同步更新操作 | |
setAcl path acl | 设置节点acl,格式为:scheme:id:permissions |
|
getAcl path | 获取节点acl信息,如getAcl /node1 |
|
listquota path | 显示节点配额 | |
setquota [-n val1 | -b val2] path | 设置节点个数及数据长度的配额,-n为子节点个数,-b为节点数据长度 |
delquota [-n | -b] path | 删除配额,-n为子节点个数,-b为节点数据长度 |
zk端口监听命令
zk四字母组合命令
使用示例
监听端口:使用ruok,代表Are you OK?
echo ruok | nc localhost 2181
返回结果为imok,代表I'm OK
zk的ACL
ACL介绍
- 每个znode创建时都会带有一个ACL列表,其用于决定谁可以对该znode拥有哪些执行权限。
a)zk的权限控制是基于每个znode的,需要对每个znode进行权限设置;
b)每个znode支持多种权限设置;
c)子节点不会继承父节点的权限,客户端有可能无权访问父节点,但是可能可以访问它的子节点。 - ACL依赖于zk的客户端身份验证机制:
a)digest:通过用户名和密码识别客户端;
b) sals: 通过kerberos认证来识别客户端;(klist -kt
和kinit -kt
)
c)ip: 通过客户端的ip来识别客户端; - ACL权限控制,可使用
scheme:id:permission
来识别:
a)权限模式(scheme):鉴权策略;
b)授权对象(id)
c)权限(permission)
ACL详解
- scheme:zk内置一些权限控制方案,可以用以下方案对每个znode进行权限设置。
方案 | 说明 |
---|---|
world | 只有一个用户:anyone,代表所有人。(默认权限) |
ip | 使用ip地址认证 |
auth | 使用已添加认证的用户认证 |
digest | 使用用户名:密码 方式认证 |
- id:授权对象id是指权限赋予的用户或者实体,如ip地址或者机器,授权模式schemoa与授权对象id的关系如下:
权限模式 | 授权对象 |
---|---|
ip | 通常是一个ip地址或者ip段,如"192.168.0.10"或"192.168.0.1/24" |
digest | 自定义,通常是“username:BASE64(SHA-1(username:password))”,如“userA:jfCYslg7DFSjtoeFAdfs7FD=” |
world | 只有一个id:anyone |
super | 与digest模式一致 |
- 权限permission
权限 | ACL简写 | 说明 |
---|---|---|
CREATE | c | 可以创建子节点,create |
DELETE | d | 可以删除子节点(仅仅是下一级节点),delete |
READ | r | 可以读取节点数据及显示子节点列表,getChildren、getData |
WRITE | w | 可以设置节点数据,setData |
ADMIN | a | 可以设置节点访问控制权限列表,setACL |
命令 | 使用方式 | 描述 |
---|---|---|
getAcl | getAcl |
读取ACL权限 |
setAcl | setAcl |
设置ACL权限 |
addauth | addauth |
设置认证 |
refer by >
官网:https://zookeeper.apache.org
《Hadoop权威指南-第四版》