Zookeeper
2022-09-22 22:47:28

Zookeeper

  1. 基于zk集群实现分布式配置中心

1. Zookeeper 是什么?

简单来说zookeeper=文件系统 + 监听通知机制

1.1 文件系统

文件系统就是一个节点树,最头上的是根,

再往下就有各级目录,每个目录中或者是文件或者是下一级的目录

zk06.png

每个子目录项如 NameService 都被称作为 znode(目录节点),

和文件系统一样,我们能够自由的增加、删除znode,在一个znode下增加、删除子znode,

唯一的不同在于znode是可以存储数据的

需要额外注意以下几点:

  1. znode 中的数据可以有多个版本,在查询该 znode 数据时就需要带上版本信息。(set path version / delete path version)

  2. znode 可以是临时 znode(create -e 生成的节点),一旦创建这个 znode 的 client 与server 断开连接,该znode 将被自动删除。client 和 server 之间通过 heartbeat 来确认连接正常,这种状态称之为 session,断开连接后 session 失效。

  3. 临时 znode 不能有子 znode。

  4. znode 可以自动编号(create -s 生成的节点),例如在 create -s /app/node 已存在时,将会生成/app/node00***001 节点。

  5. znode 可以被监控,该目录下某些信息的修改,例如节点数据、子节点变化等,可以主动通知监控注册的 client。事实上,通过这个特性,可以完成许多重要应用,例如配置管理、信息同步、分布式锁等等。

1.2 监听通知机制

客户端注册监听它关心的目录节点,当目录节点发生变化(数据改变、被删除、子目录节点增加删除)时,zookeeper会通知客户端。

zookeeper功能非常强大,可以实现诸如分布式应用配置管理、统一命名服务、状态同步服务、集群管理等功能,

我们这里拿比较简单的分布式应用配置管理为例来说明。

假设我们的程序是分布式部署在多台机器上,如果我们要改变程序的配置文件,

需要逐台机器去修改,非常麻烦,现在把这些配置全部放到zookeeper上去,

保存在 zookeeper 的某个目录节点中,然后所有相关应用程序对这个目录节点进行监听,

一旦配置信息发生变化,每个应用程序就会收到 zookeeper 的通知,然后从 zookeeper 获取新的配置信息应用到系统中。

zk07.png

每个ZNode上可存储少量数据(默认是1M, 可以通过配置修改, 通常不建议在ZNode上存储大量的数据)

配置文件文件大的话,个人建议不要用Zookeeper作为配置中心

1.3 概念

先来看看官网的解释:

zk05

关键信息:ZooKeeper是用于维护配置信息,命名,提供分布式同步以及提供组服务的集中式服务

配置管理

在我们的应用中除了代码外,还有一些就是各种配置。比如数据库连接等。一般我们都是使用配置文件的方式,在代码中引入这些配置文件。但是当我们只有一种配置,只有一台服务器,并且不经常修改的时候,使用配置文件是一个很好的做法,但是如果我们配置非常多,有很多服务器都需要这个配置,而且还可能是动态的话使用配置文件就不是个好主意了。这个时候往往需要寻找一种集中管理配置的方法,我们在这个集中的地方修改了配置,所有对这个配置感兴趣的都可以获得变更。比如我们可以把配置放在数据库里,然后所有需要配置的服务都去这个数据库读取配置。但是,因为很多服务的正常运行都非常依赖这个配置,所以需要这个集中提供配置服务的服务具备很高的可靠性。一般我们可以用一个集群来提供这个配置服务,但是用集群提升可靠性,那如何保证配置在集群中的一致性呢? 这个时候就需要使用一种实现了一致性协议的服务了。Zookeeper就是这种服务,它使用Zab这种一致性协议来提供一致性。现在有很多开源项目使用Zookeeper来维护配置,比如在HBase中,客户端就是连接一个Zookeeper,获得必要的HBase集群的配置信息,然后才可以进一步操作。还有在开源的消息队列Kafka中,也使用Zookeeper来维护broker的信息。在Alibaba开源的SOA框架Dubbo中也广泛的使用Zookeeper管理一些配置来实现服务治理。

名字服务

名字服务这个就很好理解了。比如为了通过网络访问一个系统,我们得知道对方的IP地址,但是IP地址对人非常不友好,这个时候我们就需要使用域名来访问。但是计算机是不能是别域名的。怎么办呢?如果我们每台机器里都备有一份域名到IP地址的映射,这个倒是能解决一部分问题,但是如果域名对应的IP发生变化了又该怎么办呢?于是我们有了DNS这个东西。我们只需要访问一个大家熟知的(known)的点,它就会告诉你这个域名对应的IP是什么。在我们的应用中也会存在很多这类问题,特别是在我们的服务特别多的时候,如果我们在本地保存服务的地址的时候将非常不方便,但是如果我们只需要访问一个大家都熟知的访问点,这里提供统一的入口,那么维护起来将方便得多了。

分布式锁

其实在第一篇文章中已经介绍了Zookeeper是一个分布式协调服务。这样我们就可以利用Zookeeper来协调多个分布式进程之间的活动。比如在一个分布式环境中,为了提高可靠性,我们的集群的每台服务器上都部署着同样的服务。但是,一件事情如果集群中的每个服务器都进行的话,那相互之间就要协调,编程起来将非常复杂。而如果我们只让一个服务进行操作,那又存在单点。通常还有一种做法就是使用分布式锁,在某个时刻只让一个服务去干活,当这台服务出问题的时候锁释放,立即fail over到另外的服务。这在很多分布式系统中都是这么做,这种设计有一个更好听的名字叫Leader Election(leader选举)。比如HBase的Master就是采用这种机制。但要注意的是分布式锁跟同一个进程的锁还是有区别的,所以使用的时候要比同一个进程里的锁更谨慎的使用。

这段描述不够清晰,以后重新写一下

集群管理

在分布式的集群中,经常会由于各种原因,比如硬件故障,软件故障,网络问题,有些节点会进进出出。有新的节点加入进来,也有老的节点退出集群。这个时候,集群中其他机器需要感知到这种变化,然后根据这种变化做出对应的决策。比如我们是一个分布式存储系统,有一个中央控制节点负责存储的分配,当有新的存储进来的时候我们要根据现在集群目前的状态来分配存储节点。这个时候我们就需要动态感知到集群目前的状态。还有,比如一个分布式的SOA架构中,服务是一个集群提供的,当消费者访问某个服务时,就需要采用某种机制发现现在有哪些节点可以提供该服务(这也称之为服务发现,比如Alibaba开源的SOA框架Dubbo就采用了Zookeeper作为服务发现的底层机制)。还有开源的Kafka队列就采用了Zookeeper作为Cosnumer的上下线管理。

更详细的描述参考此文章:https://www.yuque.com/realskrman/bi4s0n/ho3b8b

1.4 服务注册中心

Zookeeper是可以作为服务注册中心的,经常和Dubbo来整合使用。

除了Zookeeper,还有其他的注册中心:

  • eureka:SpringCLoud项目产品
  • nacos:阿里产品,springcloud-alibaba集成
  • consul:HashiCorp 公司推出的开源工具
  • 等..

2. 安装

2.1 单机测试

2.1.1 WIndow环境

其实很简单,参考我的这个文章:Dubbo + Zookeeper

2.1.2 Linux环境

1、下载 首先我们下载稳定版本的zookeeper http://zookeeper.apache.org/releases.html

2、上传 下载完成后,将zookee er压缩包zookeeper-3.4.14.tar.gz上传到linux系统

3、解压缩压缩包

1
tar -zxvf zookeeper-3.4.14.tar.gz

4、进入zookeeper-3.4.14目录,创建data文件夹

1
cd zookeeper-3.4.14 mkdir data

5、修改配置文件名称(因为Zookeeper默认读取的是 zoo.cfg文件)

1
2
cd conf
mv zoo_sample.cfg zoo.cfg

5、修改zoo.cfg中的data属性

1
dataDir=/root/zookeeper-3.4.14/data

6、zookeeper服务启动 进入bin目录,输入命令启动服务

1
./zkServer.sh start

输出以下内容表示启动成功

zk08.png

关闭服务输入命令

1
./zkServer.sh stop

输出以下提示信息

zk09.png

查看状态

1
./zkServer.sh status

如果启动状态,提示:

image.png

如果未启动状态,提示:

image.png

2.2 伪集群模式

这里介绍Linux搭建(WIndos也可以,步骤类似)

首先来说并不太复杂,和我的Redis的伪集群搭建类似

大致步骤:1.复制修改三份配置文件,2. 启动三份服务

Zookeeper不但可以在单机上运行单机模式,Zookeeper,而且可以在单机模拟集群模式Zookee per的运行,也就是将不同实例运行在同一台机器,用端口进行区分,伪集群模式为我们体验Zookeeper和做一 些尝试性的实验提供了很大的便利。比如,我们在测试的时候,可以先使用少量数据在伪集群模式下进行测试。当测试可行的时候,再将数据移植到集群模式进行真实的数据实验。这样不但保证了它的可行 性,同时大大提高了实验的效率。这种搭建方式,比较简便,成本比较低,适合测试和学习

注意事项 :

—台机器上部署了 3个server,也就是说单台机器及上运行多个Zookeeper实例。这种情况下,必须保 证每个配置文档的各个端口号不能冲突,除client Port不同之外,dataDir也不同。另外,还要在 dataDir所对应的目录中创建myid文件来指定对应的Zookee per服务器实例

client Port 端口:

如果在1台机器上部署多个server,那么每台机器都要不同的client Port,比如serverl是2181,server2 是2182, server3是2183

dataDir 和 dataLogDir:

dataDi r和dataLogDir也需要区分下,将数据文件和日志文件分开存放,同时每个server的这两变量所 对应的路径都是不同的

server.X 和 yid:

server.X这个数字就是对应,data/myid中的数字。在3个server的myid文件中分别写入了1, 2, 3,那 么每个serve r中的zoo.cfg都配server.1 server.2,server.3就行了。因为在同一台机器上,后面连着的2 个端口,3个server都不要一样,否则端口冲突

下载

首先我们下载最新稳定版本的 zookee per htt p://zookee per.a pache.org/releases.html 上传 下载完成后,将zookee per压缩包zookee per-3.4.14.tar.gz上传到linux系统 解压压缩包 创建目录zkcluster

1
mkdir zkcluster

解压zookee per-3・4・14・tar・gz到zkcluster目录下

1
tar -zxvf zookeeper-3.4.14.tar.gz -C /zkcluster

改变名称

1
mv zookeeper-3.4.14 zookeeper01

复制并改名

1
cp -r zookeeper01/ zookeeper02 cp -r zookeeper01/ zookeeper03

分别在zookeeper01、zookeeper02、zookeeper03目录下创建data及logs目录

1
2
3
mkdir data
cd data
mkdir logs

修改配置文件名称

1
2
cd conf
mv zoo_sample.cfg zoo.cfg

配置每一个 Zookee per 的 dataDir (zoo.cfg) client Port 分别为2181 2182 2183

1
2
3
4
5
6
7
8
clientPort=2181
dataDir=/zkcluster/zookeeper01/data
dataLogDir=/zkcluster/zookeeper01/data/logs
clientPort=2182
dataDir=/zkcluster/zookeeper02/data
dataLogDir=/zkcluster/zookeeper02/data/logs
clientPort=2183
dataDir=/zkcluster/zookeeper03/data dataLogDir=/zkcluster/zookeeper03/data/logs

配置集群

⑴ 在每个zookeeper的data目录下创建一个myid文件,内容分别是1、2、3。这个文件就是记录 每个服务器的ID

1
touch myid

(2) 在每一个zookeeper的zoo.cfg配置客户端访问端口(clientPort)和集群服务器IP列表。

1
2
3
server.1=10.211.55.4:2881:3881
server.2=10.211.55.4:2882:3882
server.3=10.211.55.4:2883:3883

# server.服务器ID=服务器IP地址:服务器之间通信端口:服务器之间投票选举端口

启动集群 依次启动三个zk实例

节点状态查看

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
node1
# ./zkServer.sh status
ZooKeeper JMX enabled by default
Using config: /app/zookeeper/bin/../conf/zoo.cfg
Mode: follower

#node2
# ./zkServer.sh status
ZooKeeper JMX enabled by default
Using config: /app/zookeeper/bin/../conf/zoo.cfg
Mode: leader

#node3
# ./zkServer.sh status
ZooKeeper JMX enabled by default
Using config: /app/zookeeper/bin/../conf/zoo.cfg
Mode: follower

2.3 真集群模式

也就是相当于类似上面伪集群的操作。不过不是在一旦机器上配置三份。而是在三台服务器上配置三份。

3. 配置文件

上面的配置基本都是在 zoo.cfg,这里来解释一下配置文件的作用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
# cp conf/zoo_sample.cfg conf/zoo.cfg
#vim zoo.cfg
# zoo.cfg文件中内容如下
# The number of milliseconds of each tick
#Zookeeper 服务器之间或客户端与服务器之间维持心跳的时间间隔,每隔tickTime时间就会发送一个心跳,
#单位毫秒,最小的session过期时间为tickTime的两倍,ZK中的其他时间都是以这个为倍数的。
tickTime=2000

# The number of ticks that the initial
# synchronization phase can take
#表示允许从服务器(相对于leader来说的客户端)连接到leader并完成数据同步的时间,
#它是以tickTime的倍数来表示的,也就是从服务器与主服务器完成初始化连接和数据同步是能够容忍多少个心跳时间,
#如果超过这个时间不能完成初始化连接的建立则表示连接失败。默认是10.
#如果你的数据量过大而且从服务器数量也多那么这个值可以设置大一点。
#总的时间长度就是 initLimit * tickTime 秒。
initLimit=10

#用于配置Leader服务器是否接受客户端的连接,是否允许Leader向客户端直接提供服务,默认是可以的。
#LeaderServes

# The number of ticks that can pass between
# sending a request and getting an acknowledgement
#配置 Leader 与 Follower 之间发送消息,请求和应答时间长度,最长不能超过多少个 tickTime 的时间长度,
#总的时间长度就是 syncLimit * tickTime 秒 ,如果在这个时间内从服务器不能与主服务器通信,
#则表示该从服务器失败。默认为5.如果集群环境网络不佳可以调整大一点。
syncLimit=5

# the directory where the snapshot is stored.
# do not use /tmp for storage, /tmp here is just
# example sakes.
#Zookeeper 保存数据的数据库快照的位置,默认是/tmp/zookeeper,
#快照文件并不是实时的,运行一段时间才会有。

#事务日志日志路径,若没提供的话则用dataDir。zookeeper的持久化都存储在这两个目录里,
#对ZK的读和写都是在内存中完成,所以速度非常快,但是如果停止ZK再启动数据还是需要保证的,
#所以就会有这样一个路径用来保存事务日志,当ZK再次启动时加载到内存重演过程来恢复数据。
#dataLogDir里是放到的顺序日志(WAL),指定的目录下有version-2文件夹(下有log.1文件),
#这个目录确定了当前事务日志的版本号,当下次某个版本的ZK对其进行修改时,版本号发生变化。
#日志文件大小为64M,如果数据比较多就会有多个这样大小的文件。而dataDir里放的是内存数据结构的snapshot,
#便于快速恢复。为了达到性能最大化,一般建议把dataDir和dataLogDir分到不同的磁盘上,
#建议将事物日志保存到单独的磁盘而且是高速磁盘。因为为了一致性,
#ZK对于客户端的写入请求在返回之前就要把本次操作写入到事物日志中
dataDir=/app/zookeeper/data
dataLogDir=/app/zookeeper/logs

# the port at which the clients will connect
#Zookeeper服务器监听的端口,以接受客户端的访问请求。
clientPort=2181

# the maximum number of client connections.
# increase this if you need to handle more clients
#限制连接到ZK上的客户端数量,并且限制并发连接数量,它通过IP来区分不同客户端。
#值为0表示不做任何限制。注意这里的限制是针对单台客户端到服务器的,并不是控制所有客户端连接的。默认60.
#maxClientCnxns=60

最小会话超时时间,默认为tickTime的2倍。不建议把这个值设置的比tickTime小。客户端连接到ZK时如果在这个最小时间内没有和ZK联系则标记为超时,也就是说会断开。
#minSessionTimeout=120

最大会话超时时间,默认为20倍的最小会话超时时间。不建议把这个值设置的比tickTime小。客户端连接到ZK时如果在这个最大时间内没有和ZK联系则标记为超时。所以上面的参数和这个参数组成了一个时间范围,也就是客户端连接ZK时如果在这个时间范围内没有成功连接则会标记为超时。如果客户端设置的时间范围不在这个服务器设置的范围内,则会被强制应用服务器设置的范围。
#maxSessionTimeout=2400

# Be sure to read the maintenance section of the
# administrator guide before turning on autopurge.
# http://zookeeper.apache.org/doc/current/zookeeperAdmin.html#sc_maintenance
# The number of snapshots to retain in dataDir
#自动清理日志,该参数设置保留多少个快照文件和对应的事务日志文件,默认为3,
#如果你设置的小于3则会被自动的调整为3
#autopurge.snapRetainCount=3

# Purge task interval in hours
# Set to "0" to disable auto purge feature
#自动清理日志,该参数设置自动清理频率,上面的参数配套使用。
#客户端在和ZK服务器交互中服务器会产生很多日志,而且ZK会将内存中的数据作为快照保存起来,
#而且这些数据不会自动删除,那么磁盘空间就会被占用,可以设置这2个参数来自动清理,
#不过如果ZK服务器比较繁忙而且赶上删除日志任务就会影响性能,所以一般不设置这个自动清理,
#而是在ZK访问量少的时候通过Linux的定时任务来处理。0表示不开启自动清理功能。
#autopurge.purgeInterval=1

#ZK的最大请求堆积数,客户端请求比较多,为了防止客户端资源过度消耗,服务器必须限制同时处理的请求数量。
#globalOutstandingLimit

#用于配置ZK事务日志预先分配的空间,默认是64M
#preAllocSize

#用于配置相邻两次快照之间的事物日志次数,默认是10万。也就是10万条事务之后做一次快照同时结转事务日志
#snapCount

#这个参数针对多网卡的ZK服务器,允许为每个IP地址指定不同的监听端口。
#clientPortAddres

#用于设置ZK服务器事物日志同步操作时消耗时间的报警阈值,如果实际消耗时长超过这个时间日志就会记录。
#fsync.warningthresholdms

#用于配置Leader选举算法,目前只有一种选举算法,所以不用配置。
#electionAlg

#用于Leader选举时各个服务器之间进行的TCP连接创建超时时间,默认为5.
#cnxTimeout

#这个参数用于配置ZK服务器是否在事物提交时是否强制写入磁盘(LINUX的延迟写入),默认是YES。
#forceSync

#用于配置单个数据节点上最大数量,默认是1MB。通常不需要改动该参数,但是因为Zookeeper不适合存放太多数据所以有时候需要把值改小
#jute.maxbuffer

#是否跳过ACL检查,默认是no,也就是会对所有客户端连接进行acl检查。
#skipACL

#其中X是一个数字, 表示这是第几号server,它的值和myid文件中的值对应。
#A是该server所在的IP地址。B是配置该server和集群中的leader交换消息所使用的端口。
#C配置选举leader时所使用的端口。由于配置的是伪集群模式,所以各个server的B, C参数必须不同,
#如果是真正分布式集群,那么B和C在各个节点上可以相同,
#因为即使相同由于节点处于不同的服务器也不会导致端口冲突。
#格式server.X=A:B:C
server.1=10.55.2.81:2888:3888
server.2=10.55.2.82:2888:3888
server.3=10.55.2.83:2888:3888

4. 系统模型

ZooKee per 数据模型 Znode 在ZooKeeper中,数据信息被保存在一个个数据节点上,这些节点被称为znode。ZNode是Zookee per中最小数据单位,在ZNode下面又可以再挂ZNode,这样一层层下去就形成了一个层次化 命名空间ZNode树,我们称为ZNode Tree,它采用了类似文件系统的层级树状结构进行管理。见下图 示例:

zk12.png

在Zookeeper中,每一个数据节点都是一个ZNode,上图根目录下有两个节点,分别是;app1和 app2,其中app1下面又有三个子节点,所有ZNode按层次化进行组织,形成这么一颗树,ZNode的节 点路径标识方式和Unix文件系统路径非常相似,都是由一系列使用斜杠(/)进行分割的路径表示,开 发人员可以向这个节点写入数据,也可以在这个节点下面创建子节点。

4.1 ZNode类型

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

Zookee per节点类型可以分为三大类:

  • 持久性节点(Persistent)
  • 临时性节点(Ephemeral)
  • 顺序性节点(Sequential))

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

持久节点:

是Zookeeper中最常见的一种节点类型,所谓持久节点,就是指节点被创建后会一直存在服 务器,直到删除操作主动清除

持久顺序节点:

就是有顺序的持久节点,节点特性和持久节点是一样的,只是额外特性表现在顺序上。 顺序特性实质是在创建节点的时候,会在节点名后面加上一个数字后缀,来表示其顺序。

临时节点:

就是会被自动清理掉的节点,它的生命周期和客户端会话绑在一起,客户端会话结束,节点会被删除掉。与持久性节点不同的是,临时节点不能创建子节点

临时顺序节点

就是有顺序的临时节点,和持久顺序节点相同,在其创建的时候会在名字后面加上数字 后缀。

事务ID

首先,先了解,事务是对物理和抽象的应用状态上的操作集合。往往在现在的概念中,狭义上的事务通 常指的是数据库事务,一般包含了一系列对数据库有序的读写操作,这些数据库事务具有所谓的ACID特 性,即原子性(Atomic)、一致性(Consistency)、隔离性(Isolation)和持久性(Durability)。

而在ZooKeeper中,事务是指能够改变ZooKeeper服务器状态的操作,我们也称之为事务操作或更新操作,一般包括数据节点创建与删除、数据节点内容更新等操作。对于每一个事务请求,ZooKeeper都会 为其分配一个全局唯一的事务ID,用 **ZXID **来表示,通常是一个64位的数字。每一个ZXID对应一次更新操作,从这些ZXID中可以间接地识别出ZooKeeper处理这些更新操作请求的全局顺序

4.2 ZNode的状态信息

我先来写几个命令

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#根目录的所有节点
[zk: localhost:2181(CONNECTED) 5] ls /
[dubbo, test, zookeeper]

# get 命令 来获取节点内容
[zk: localhost:2181(CONNECTED) 6] get /test
hope666

# stat 命令 来获取节点状态信息
[zk: localhost:2181(CONNECTED) 4] stat /test
cZxid = 0x5d
ctime = Wed Dec 09 13:46:38 GMT+08:00 2020
mZxid = 0x5e
mtime = Wed Dec 09 13:47:01 GMT+08:00 2020
pZxid = 0x61
cversion = 2
dataVersion = 1
aclVersion = 0
ephemeralOwner = 0x0
dataLength = 7
numChildren = 0

整个ZNode节点内容包括两部分:节点数据内容和节点状态信息。那么这些状态信息都有什么含义呢?

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

4.3 Watcher

Watcher 数据变更通知

Zookeeper使用Watcher机制实现分布式数据的发布/订阅功能 —个典型的发布/订阅模型系统定义了一种一对多的订阅关系,能够让多个订阅者同时监听某一个主题 对象,当这个主题对象自身状态变化时,会通知所有订阅者,使它们能够做出相应的处理。

在ZooKeeper中,引入了 Watcher机制来实现这种分布式的通知功能。ZooKeeper允许客户端向服务 端注册一个Watcher监听,当服务端的一些指定事件触发了这个Watche r,那么就会向指定客户端发 送一个事件通知来实现分布式的通知功能。

整个Watcher注册与通知过程如图所示。

zk13.png

Zookeeper的Watcher机制主要包括客户端线程、客户端WatcherManager、Zookeeper服务器三部 分。

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

这种监听机制,把压力分散在了每一个服务器上,去监听这个zookeeper中心。监听数据变化

4.4 ACL

ACL–保障数据的安全

Zookee per作为一个分布式协调框架,其内部存储了分布式系统运行时状态的元数据,这些元数据会直 接影响基于Zookee per进行构造的分布式系统的运行状态,因此,如何保障系统中数据的安全,从而避 免因误操作所带来的数据随意变更而导致的数据库异常十分重要,在Zookee per中,提供了一套完善的 ACL (Access Control List)权限控制机制来保障数据的安全。

我们可以从三个方面来理解ACL机制:**权限模式(Scheme)、授权对象(ID)、权限 (Permission),通常使用scheme: id : permission**来标识一个有效的ACL信息。

权限模式:Scheme

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

ip

IP模式就是通过IP地址粒度来进行权限控制,如”i p:192.168.0.110”表示权限控制针对该IP地址, 同时IP模式可以支持按照网段方式进行配置,如”ip :192.168.0.1/24”表示针对192.168.0.*这个网段 进行权限控制。

Digest

Digest是最常用的权限控制模式,要更符合我们对权限控制的认识,其使

用”username: password”形式的权限标识来进行权限配置,便于区分不同应用来进行权限控制。

当我们通过”username:password”形式配置了权限标识后,Zookeeper会先后对其进行SHA-1加密 和BASE64编码。

World

World是一种最开放的权限控制模式,这种权限控制方式几乎没有任何作用,数据节点的访问权限 对所有用户开放,即所有用户都可以在不进行任何权限校验的情况下操作ZooKee per上的数据。 另外,World模式也可以看作是一种特殊的Digest模式,它只有一个权限标识,即”world: anyone”。

Super

Super模式,顾名思义就是超级用户的意思,也是一种特殊的Digest模式。在Super模式下,超级 用户可以对任意ZooKee per上的数据节点进行任何操作。

授权对象:ID

授权对象指的是权限赋予的用户或一个指定实体,例如IP地址或是机器等。在不同的权限模式下,授 权对象是不同的,表中列出了各个权限模式和授权对象之间的对应关系。

zk14.png

权限

权限就是指那些通过权限检查后可以被允许执行的操作。在ZooKee per中,所有对数据的操作权限分为 以下五大类:

  • CREATE (C):数据节点的创建权限,允许授权对象在该数据节点下创建子节点。

  • DELETE (D): 子节点的删除权限,允许授权对象删除该数据节点的子节点。

  • READ (R):数据节点的读取权限,允 许授权对象访问该数据节点并读取其数据内容或子节点列表等。

  • WRITE (W):数据节点的更新权 限,允许授权对象对该数据节点进行更新操作。

  • ADMIN (A):数据节点的管理权限,允许授权对象 对该数据节点进行ACL相关的设置操作。

5. 命令

现在已经搭建起了一个能够正常运行的zookee per服务了,所以接下来,就是来借助客户端来对 zookee per的数据节点进行操作 首先,进入到zookee per的bin目录之后 通过zkClient进入zookee per客户端命令行

./zkcli.sh 连接本地的zookee per服务器

./zkCli.sh -server ip:p ort连接指定的服务器

连接成功之后,系统会输出Zookee per的相关环境及配置信息等信息。输入hel p之后,屏幕会输出可用 的Zookeeper命令,如下图所示

1、创建节点

使用Create命令,可以•创建一个Zookee per节点,如

1
create [-s][-e] path data acl

其中,-s或-e分别指定节点特性,顺序或临时节点,若不指定,则创建持久节点;acl用来进行权限控制。

2、创建顺序节点

1
create -s /zk-test 123

执行完后,就在根节点下创建了一个叫做/zk-test的节点,该节点内容就是123,同时可以看到创建的 zk-test节点后面添加了一串数字以示区别

3、创建临时节点

使用create -e /zk-temp 123命令创建zk-te mp临时节

1
create -e /zk-temp 123

临时节点在客户端会话结束后,就会自动删除,下面使用****quit****命令退出客户端

再次使用客户端连接服务端,并使用ls /命令查看根目录下的节点

可以看到根目录下已经不存在zk-tem P临时节点了

4、创建永久节点

使用 create /zk-p ermanent 123 命令创建 zk- permanent 永久节点

1
create /zk-permanent 123

可以看到永久节点不同于顺序节点,不会自动在后面添加一串数字

5、读取节点

与读取相关的命令有ls命令和get命令 ls命令可以列出Zookee per指定节点下的所有子节点,但只能查看指定节点下的第一级的所有子节点;

1
ls path

其中,path表示的是指定数据节点的节点路径 get命令可以获取Zookee per指定节点的数据内容和属性信息。

1
get path

若获取根节点下面的所有子节点,使用*ls* ****/****命令即可

若想获取/zk-permanent的数据内容和属性,可使用如下命令:

1
get /zk-permanent

我们可以看到,第一行是节点/zk-permanent的数据内容,其他几行则是创建该 节点的事务ID (cZxid)、最后一次更新该节点的事务ID (mZxid)和最后一次更新该节点的时间(mtime)等属性信息

6、更新节点

使用set命令,可以更新指定节点的数据内容,用法如下

1
set path data [version]

其中,data就是要更新的新内容,version表示数据版本,在zookeeper中,节点的数据是有版本概 念的,这个参数用于指定本次更新操作是基于Znode的哪一个数据版本进行的,如将/zk-P ermanent节 点的数据更新为456,可以使用如下命令:

1
set /zk-permanent 456

现在dataVersion已经变为1 了,表示进行了更新

7、删除节点

使用delete命令可以删除Zookee per上的指定节点,用法如下

1
delete path [version]

其中version也是表示数据版本,使用delete /zk- permanent命令即可删除/zk-p ermanent节点

值得注意的是,若删除节点存在子节点,那么无法删除 该节点,必须先删除子节点,再删除父节点

6. API

也就是通过JAVA来操作我们的Zookeeper,而不是直接通过Zookeeper客户端的命令行。

可以参考这篇文章:https://www.yuque.com/realskrman/bi4s0n/sf8hyq#VvaWB

7. 服务器角色

Leader

Leader服务器是Zookee per集群工作的核心,其主要工作有以下两个:

(1) 事务请求的唯一调度和处理者,保证集群事务处理的顺序性。

(2) 集群内部各服务器的调度者。

\1. 请求处理链

使用责任链来处理每个客户端的请求是Zookeeper的特色,Leader服务器的请求处理链如下:

zk15.png

可以看到,从 P re pRequest Processor 到 FinalRequest Processor 前后一共 7个请求处理器组成 了 leader 服务器的请求处理链

(1) Prep Request Processor。请求预处理器,也是leader服务器中的第一个请求处理器。在Zookee per 中,那些会改变服务器状态的请求称为事务请求(创建节点、更新数据、删除节点、创建会话等), Prep Request Process。r能够识别出当前客户端请求是否是事务请求。对于事务请求,

PrepRequestProcess。r处理器会对其进行一系列预处理,如创建请求事务头、事务体、会话检查、ACL 检查和版本检查等。

(2) ProposalRequestProcessor。事务投票处理器。也是Leader服务器事务处理流程的发起者,对 于非事务性请求,Propo salRequest Process。r会直接将请求转发到Commit Processor处理器,不再做 任何处理,而对于事务性请求,处理将请求转发到Commit Processor外,还会根据请求类型创建对应的 Pro po sal提议,并发送绐所有的Follower服务器来发起一次集群内的事务投票。同时, Propo salRequest Processor还会将事务请求交付绐SyncRequest Processor进行事务日志的记录。

(3) SyncRequest Processor。事务日志记录处理器。用来将事务请求记录到事务日志文件中,同时 会触发Zookee per进行数据快照。

(4) AckRequestProcessor。负责在SyncRequestProcessor完成事务日志记录后,向Proposal的投 票收集器发送ACK反馈,以通知投票收集器当前服务器已经完成了对该Proposal的事务日志记录。

(5) Commit Processor。事务提交处理器。对于非事务请求,该处理器会直接将其交付绐下一级处 理器处理;对于事务请求,其会等待集群内针对Prop osa l的投票直到该Prop osal可被提交,利用 Commit Processor,每个服务器都可以很好地控制对事务请求的顺序处理。

(6) ToBeCommit Processor。该处理器有一个toBeA pp lied队列,用来存储那些已经被

Commit Processor处理过的可被提交的P ro posal。其会将这些请求交付绐FinalRequest Processor处理 器处理,待其处理完后,再将其从toBeA PP lied队列中移除。

(7) FinalRequest Processor。用来进行客户端请求返回之前的操作,包括创建客户端请求的响应, 针对事务请求,该处理器还会负责将事务应用到内存数据库中。

Follower

Follower服务器是Zookee per集群状态中的跟随者,其主要工作有以下三个:

(1)处理客户端非事务性请求(读取数据),转发事务请求绐Leader服务器。

(1) 参与事务请求P ro po sa l的投票。

(2) 参与Leader选举投票。

和leader—样,Follower也采用了责任链模式组装的请求处理链来处理每一个客户端请求,由于不 需要对事务请求的投票处理,因此Follower的请求处理链会相对简单,其处理链如下

zk16.png

和Leader服务器的请求处理链最大的不同点在于,Follower服务器的第一个处理器换成了 FollowerRequest Processor处理器,同时由于不需要处理事务请求的投票,因此也没有了 P ro po salRequest Processor 处理器。

(1) FollowerRequestProcessor 其用作识别当前请求是否是事务请求,若是,那么Follower就会将该请求转发绐Leader服务器, Leader服务器在接收到这个事务请求后,就会将其提交到请求处理链,按照正常事务请求进行处理。

(2) SendAckRequestProcessor 其承担了事务日志记录反馈的角色,在完成事务日志记录后,会向Leader服务器发送ACK消息以表明自 身完成了事务日志的记录工作

Observer

Observer是ZooKee per自3.3.0版本开始引入的一个全新的服务器角色。从字面意思看,该服务器充当 了一个观察者的角色其观察ZooKee per集群的最新状态变化并将这些状态变更同步过来。

Observer服务器在工作原理上和Follower基本是一致的,对于非事务请求,都可以进行独立的处理,而 对于事务请求,则会转发绐Leader服务器进行处理。和Follower唯一的区别在于,Observer不参与任 何形式的投票,包括事务请求Proposal的投票和Leader选举投票。简单地讲,Observer服务器只提供 非事务服务,通常用于在不影响集群事务处理能力的前提下提升集群的非事务处理能力。

另外,Observer的请求处理链路和Follower服务器也非常相近,其处理链如下

zk17.png

另外需要注意的一点是,虽然在图中可以看到,Observer服务器在初始化阶段会将

SyncRequestProcess。r处理器也组装上去,但是在实际运行过程中,Leader服务器不会将事务请求的 投票发送绐Observer服务器。

8. 选举机制

1.初始化Leader选举。

集群模式特有,Zookeeper首先会根据自身的服务器ID(SID)、最新的

ZXID (lastLoggedZxid)和当前的服务器epoch (currentEpoch)来生成一个初始化投票,在 初始化过程中,每个服务器都会给自己投票。然后,根据zoo.cfg的配置,创建相应Leader选举算法 实现,Zookeeper提供了三种默认算法(LeaderElection、AuthFastLeaderElection、 FastLeaderElection),可通过zoo.cfg中的electionAlg属性来指定,但现只支持 FastLeaderElection选举算法。在初始化阶段,Zookeeper会创建Leader选举所需的网络I/O层 QuorumCnxManager,同时启动对Leader选举端口的监听,等待集群中其他服务器创建连接。

2.注册JMX服务。

3 检测当前服务器状态

运行期间,Quorum Peer会不断检测当前服务器状态。在正常情况下,Zookee per服务器的状态 在 LOOKING、LEADING、FOLLOWING/OBSERVING 之间进行切换。在启动阶段,Quorum Peer 的初始 状态是LOOKING,因此开始进行Leader选举。

4. Leader 选举

ZooKee per的Leader选举过程,简单地讲,就是一个集群中所有的机器相互之间进行一系列投 票,选举产生最合适的机器成为Leader,同时其余机器成为Follower或是Observer的集群机器角 色初始化过程。关于Leader选举算法,简而言之,就是集群中哪个机器处理的数据越新(通常我们根 据每个服务器处理过的最大ZXID来比较确定其数据是否更新),其越有可能成为Leader。当然,如 果集群中的所有机器处理的ZXID—致的话,那么SID最大的服务器成为Leader,其余机器称为 Follower 和 Observer

9 参考文献

Prev
2022-09-22 22:47:28
Next