Zookeeper知识点
分布式协调框架Zookeeper
- 知识点结构图
一、Zookeeper概述
1.1 定义
ZooKeeper是一个分布式的,开放源码的,用于分布式应用程序的协调服务(service);
从设计模式角度来看,ZooKeeper是一个基于观察者模式设计的分布式服务管理框架,他负责存储和管理大家都关心的数据,然后接受观察者的注册,一旦数据发生变化就负责通知已经在ZooKeeper上注册的那些观察者做出相应的反应。
ZooKeeper = 文件系统 + 通知机制。
ZooKeeper中三个重要的逻辑:注册、监听事件、回调函数
1.2 特点
Zookeeper有攘其外,安其内的特点,也就是指它内部和谐统一,外部一致对外。
攘其外是指Zookeeper服务端有两种模式:单机的独立模式和集群的仲裁模式,所谓仲裁是指一切事件只要满足多数派同意就执行,不需要等到集群中的每个节点反馈才执行。Zookeeper本身也是服从主从架构的,在仲裁模式下会有一个投票后的节点作为Leader(领导者),而其余集群中的节点作为Follower(公民/跟随者),对某一事件的执行,只要超过半数的Follower返回执行成功,就可以开始下一步,而不需要等到所有节点都执行完毕才开始下一步。
安其内是指Zookeeper的leader选举,leader的选举会发生在集群启动时和运行中leader挂了,概括选举过程也是少数服从多数选出新leader。
其他特点:
- 集群中只要有半数以上节点存活,ZooKeeper集群就能正常服务
- 全局数据一致:每个Server保存一份相同的数据副本,Client无论连接到哪一个Server,数据都是一样的。
- 更新请求顺序进行:来自同一个Client的更新请求按其发送顺序一次进行
- 数据更新原子性:一次数据更新要么成功,要么都失败。
ZooKeeper简单易用,能够很好的解决分布式框架在运行中,出现的各种协调问题。比如集群master主备切换、节点的上下线感知等等
1.3 应用场景
- 统一命名服务:分布式环境下,对应用、服务进行统一命名。
- 统一配置管理:对配置文件修改后,希望能够快速同步到各个节点上,可将配置信息写入Zookeeper上的一个Znode,成功监听后,Zookeeper将通知各个客户端服务器
- 统一集群管理:分布式环境中,实时掌握每个节点的状态
- 服务器节点动态上下线:客户端能够实时洞察到服务器上下线的变化。
- 软负载均衡:在Zookeeper中记录每台服务器的访问数,让访问数最少的服务器去处理最新的客户端请求。
1.4 应用案例
-
NameNode使用ZooKeeper实现高可用.
-
Yarn ResourceManager使用ZooKeeper实现高可用.
-
利用ZooKeeper对HBase集群做高可用配置
-
kafka使用ZooKeeper
-
保存消息消费信息比如offset.
-
用于检测崩溃
-
主题topic发现
-
保持主题的生产和消费状态
-
二、Zookeeper选举机制
1.Zookeeper的Leader选举分为两种情况:
-
第一种,集群初始启动时
-
第二种,集群运行中的Leader挂了时
2.ZooKeeper服务器四种状态:
- looking:服务器处于寻找Leader群首的状态
- leading:服务器作为群首时的状态
- following:服务器作为follower跟随者时的状态 (会参加选举,还有读写)
- observing:服务器作为观察者时的状态 (不会参加选举,只响应客户端读写)
3.选举原则:集群中过半数Server启动后,才能选举出Leader,同时得到超半数票的服务器节点才能成为Leader;zxid大的server胜出;若zxid相等,再根据判断sid判断,sid大的胜出
2.1 集群初始启动时的Leader选举
2.1.1 简述版
假设有五台服务器组成的 Zookeeper 集群,它们的投票信息分别是(1,0)、 (2,0)、 (3,0) 、(4,0) 、(5,0),代表最开始都是投票给自己,同时它们都是最新启动的,也就是没有历史数据,在存放数据量这一点上,都是一样的。
假设这些服务器依序启动:
- (1)服务器 1 启动,此时只有它一台服务器启动了,它发出去的报文没有任何响应,所以它的选举状态一直是 LOOKING 状态。
- (2)服务器 2 启动,它与最开始启动的服务器 1 进行通信,互相交换自己的选举结果,由于zxid =0相等,所以 sid (myid)值较大的服务器 2 胜出,服务器1的选举结果更新为(2.0),但是由于没有达到超过半数以上的服务器都同意选举它(这个例子中的半数以上是 3),所以服务器 1、2 还是继续保持 LOOKING 状态。
- (3)服务器 3 启动,根据前面的理论分析,服务器 3 成为服务器 1、2、3 中的老大, 而与上面不同的是,此时有三台服务器选举了它,所以它成为了这次选举的 Leader,同时服务器3状态变为Leading
- (4)服务器 4 启动,根据前面的分析,理论上服务器 4 应该是服务器 1、2、3、4 中最大的,但是由于前面已经有半数以上的服务器选举了服务器 3,所以它只能当跟随者,状态变为Following
- (5)服务器 5 启动,同 4 一样当跟随者,状态变为Following
2.1.2 详细版
假设以3台机器组成的ZooKeeper集群为例 ,这里的quonum半数为2。
首先,每个server投票信息vote信息结构为(sid,zxid);
server1~3初始投票信息分别为:
server1 -> (1, 0)
server2 -> (2, 0)
server3 -> (3, 0)
-
leader选举公式:
server1 (sid1,zxid1)
server2 (sid2,zxid2)
zxid大的server胜出;若zxid相等,再根据判断sid判断,sid大的胜出
-
流程:
- ZK1和ZK2票投给自己;ZK1的投票为(1, 0),ZK2的投票为(2, 0),并各自将投票信息分发给其他机器。
- 处理投票。每个server将收到的投票和自己的投票对比;ZK1更新自己的投票为(2, 0),并将投票重新发送给ZK2。
- 统计投票。server统计投票信息,是否有半数server投同一个服务器为leader,由于这里只有三台,所以半数是2,则ZK2成为Leader;
- 改变服务器状态。确定Leader后,各服务器更新自己的状态,Follower变为FOLLOWING;Leader变为LEADING。
- 当K3启动时,发现已有Leader,不再选举,直接从LOOKING改为FOLLOWING。
2.2 集群运行中的Leader挂了时的选举
leader很重要?如果挂了怎么办?开始选举新的leader。
每个follower都有一个心跳倒计时,如果倒计时走完了还没有收到leader的消息就会进入looking状态,重新寻找新leader,如果收到了就刷新倒计时。
重新选举的过程如下:
前提:在运行期间,每个服务器上的ZXID可能不同,这里假设Server1是(1,3),Server3是(3,2),按照假设走Server1会成为新leader。
三、Zookeeper仲裁
ZooKeeper服务端有两种不同的运行模式。单机的称为"独立模式"(standalone mode);集群的称为“仲裁模式(quorum mode)”(少数服从多数)
部署方式也是两种:单机模式、集群模式
3.1 什么是仲裁?
仲裁指一切事件只要满足多数派同意就执行,不需要等到集群中的每个节点反馈才执行。
3.2 仲裁模式下服务器数量最好为奇数个
- 因为奇数个的服务器数据具备更好的容错性,比如5个节点和6个节点的集群,5个节点的集群能接受服务器宕机的数量为总数的五分之二,而6个节点的集群能接受的宕机数量为总数的三分之一.(如下图解)
- 第二点是因为奇数个的服务器数量需要等待仲裁同意执行的服务器少,响应更快。
四、Zookeeper安装和使用
4.1 单机模式安装部署
4.1.1 安装前准备
- 安装JDK
- 拷贝Zookeeper安装包到Linux下
- 解压到指定目录
$ tar -zxvf zookeeper-3.4.10.tar.gz -C /opt/module/
4.1.2 修改配置
# 将/opt/module/zookeeper-3.4.10/conf 这个路径下的 zoo_sample.cfg 修改为 zoo.cfg;
$ mv zoo_sample.cfg zoo.cfg
# 打开 zoo.cfg 文件,修改 dataDir 路径:
$ vim zoo.cfg
# 修改如下内容:
dataDir=/opt/module/zookeeper-3.4.10/zkData
# 在/opt/module/zookeeper-3.4.10/这个目录上创建 zkData 文件夹
$ mkdir zkData
4.1.3 操作Zookeeper
# 启动 Zookeeper
[aaa@qq.com zookeeper-3.4.10]$ bin/zkServer.sh start
# 查看进程是否启动
jps
# 查看状态
[aaa@qq.com zookeeper-3.4.10]$ bin/zkServer.sh status
# 启动客户端
[aaa@qq.com zookeeper-3.4.10]$ bin/zkCli.sh
# 退出客户端:
quit
# 停止 Zookeeper
[aaa@qq.com zookeeper-3.4.10]$ bin/zkServer.sh stop
4.2 配置参数解读
Zookeeper中的配置文件zoo.cfg中参数含义解读如下:
-
1.tickTime =2000:通信心跳数,Zookeeper 服务器与客户端心跳时间,单位毫秒
它用于心跳机制,并且设置最小的session超时时间为两倍心跳时间。(session的最小超时时间是2*tickTime)
-
2.initLimit =10:LF 初始通信时限
Leader和Follower初始通信能容忍的最多心跳数(tickTime的数量),用它来限定集群中的Zookeeper服务器连接到Leader的时限。
-
3.syncLimit =5:LF 同步通信时限
Leader和Followerr之间的最大响应时间单位,假如响应超过syncLimit * tickTime,Leader认为Follwer死掉,从服务器列表中删除Follwer。
-
4.dataDir:数据文件目录+数据持久化路径
主要用于保存 Zookeeper 中的数据。
-
5.clientPort =2181:客户端连接端口
监听客户端连接的端口。
4.3 集群模式安装部署
假设规划在 hadoop102、hadoop103 和 hadoop104 三个节点上部署 Zookeeper。
4.3.1 解压安装
# 解压 Zookeeper 安装包到/opt/module/目录下
[aaa@qq.com software]$ tar -zxvf zookeeper-3.4.10.tar.gz -C /opt/module/
# 同步/opt/module/zookeeper-3.4.10 目录内容到 hadoop103、hadoop104
[aaa@qq.com module]$ xsync zookeeper-3.4.10/
4.3.2 配置服务器编号
# 在/opt/module/zookeeper-3.4.10/这个目录下创建 zkData
[aaa@qq.com zookeeper-3.4.10]$ mkdir -p zkData
# 在/opt/module/zookeeper-3.4.10/zkData 目录下创建一个 myid 的文件
#在文件中添加与 server 对应的编号:
[aaa@qq.com zkData]$ vim myid
2
# 拷贝配置好的 zookeeper 到其他机器上
# 并分别在 hadoop102、hadoop103 上修改 myid文件中内容为 3、4
[aaa@qq.com zkData]$ xsync myid
4.3.3 配置 zoo.cfg 文件
# 重命名/opt/module/zookeeper-3.4.10/conf 这个目录下的 zoo_sample.cfg 为 zoo.cfg
[aaa@qq.com conf]$ mv zoo_sample.cfg zoo.cfg
# 打开 zoo.cfg 文件,修改数据存储路径配置
[aaa@qq.com conf]$ vim zoo.cfg
dataDir=/opt/module/zookeeper-3.4.10/zkData
# 增加如下配置
#######################cluster##########################
server.2=hadoop102:2888:3888
server.3=hadoop103:2888:3888
server.4=hadoop104:2888:3888
# 同步 zoo.cfg 配置文件
[aaa@qq.com conf]$ xsync zoo.cfg
配置参数解读:
server.A=B:C:D。
- A 是一个数字,表示这个是第几号服务器;与前面设置的myid对应,Zookeeper 启动时读取此文件,拿到里面的数据与 zoo.cfg 里面的配置信息比较从而判断到底是哪个 server。
- B 是这个服务器的 ip 地址;
- C 是这个服务器与集群中的 Leader 服务器交换信息的端口
- D 是万一集群中的 Leader 服务器挂了,需要一个端口来重新进行选举,选出一个新的Leader,而这个端口就是用来执行选举时服务器相互通信的端口。
4.3.4 集群操作
# 分别启动 Zookeeper
[aaa@qq.com zookeeper-3.4.10]$ bin/zkServer.sh start
[aaa@qq.com zookeeper-3.4.10]$ bin/zkServer.sh start
[aaa@qq.com zookeeper-3.4.10]$ bin/zkServer.sh start
# 查看状态
[aaa@qq.com zookeeper-3.4.10]# bin/zkServer.sh status
JMX enabled by default
Using config: /opt/module/zookeeper-3.4.10/bin/../conf/zoo.cfg
Mode: follower
[aaa@qq.com zookeeper-3.4.10]# bin/zkServer.sh status
JMX enabled by default Using config: /opt/module/zookeeper-3.4.10/bin/../conf/zoo.cfg
Mode: leader
[aaa@qq.com zookeeper-3.4.5]# bin/zkServer.sh status
JMX enabled by default Using config: /opt/module/zookeeper-3.4.10/bin/../conf/zoo.cfg
Mode: follower
4.4 客户端命令操作
汇总:
# 启动客户端
$ bin/zkCli.sh
# 查看当前 znode 中所包含的内容
[zk: localhost:2181(CONNECTED) 0] ls /
# 查看当前节点详细数据
[zk: localhost:2181(CONNECTED) 1] ls2 /
[zookeeper]
cZxid = 0x0
ctime = Thu Jan 01 08:00:00 CST 1970
mZxid = 0x0
mtime = Thu Jan 01
...
# 分别创建 2 个普通节点
[zk: localhost:2181(CONNECTED) 3] create /sanguo "jinlian"
Created /sanguo
[zk: localhost:2181(CONNECTED) 4] create /sanguo/shuguo "liubei"
Created /sanguo/shuguo
# 获得节点的值
# 对刚刚创建的节点查看信息
[zk: localhost:2181(CONNECTED) 5] get /sanguo
jinlian
cZxid = 0x100000003
ctime = Wed Aug 29 00:03:23 CST 2018
mZxid = 0x100000003
mtime = Wed Aug 29 00:03:23 CST 2018
pZxid = 0x100000004
...
# 创建临时节点
# quit之后再ls就看不到这个节点了。
[zk: localhost:2181(CONNECTED) 7] create -e /sanguo/wuguo "zhouyu"
Created /sanguo/wuguo
# 创建带序号的节点
# 注意结尾会带序号。
[zk: localhost:2181(CONNECTED) 2] create -s /sanguo/weiguo/xiaoqiao "jinlian"
Created /sanguo/weiguo/xiaoqiao0000000000
[zk: localhost:2181(CONNECTED) 3] create -s /sanguo/weiguo/daqiao "jinlian"
Created /sanguo/weiguo/daqiao0000000001
# 修改节点数据值
[zk: localhost:2181(CONNECTED) 6] set /sanguo/weiguo "simayi"
#节点值的变化监听
# 在 hadoop104 主机上注册监听/sanguo 节点数据变化
get /sanguo watch
# 在 hadoop103 主机上修改/sanguo 节点的数据
set /sanguo "xisi"
# 观察 hadoop104 主机收到数据变化的监听
WATCHER:: WatchedEvent
state:SyncConnected
type:NodeDataChanged path:/sanguo
# 节点的子节点变化监听(路径变化),节点103的还是上面创建的那个监听器,
[zk: localhost:2181(CONNECTED) 2] create /sanguo/jin "simayi" Created /sanguo/jin
# 观察 hadoop104 主机收到子节点变化的监听
WATCHER:: WatchedEvent
state:SyncConnected
type:NodeChildrenChanged --这里不同
path:/sanguo
# 删除节点
[zk: localhost:2181(CONNECTED) 4] delete /sanguo/jin
# 递归删除节点
[zk: localhost:2181(CONNECTED) 15] rmr /sanguo/shuguo
# 查看节点状态
[zk: localhost:2181(CONNECTED) 17] stat /sanguo
五、Zookeeper数据结构
ZooKeeper所提供的服务主要是通过以下三个部分来实现的
ZooKeeper=简约版文件系统+原语+通知机制(Watcher)。
5.1 数据节点ZNode
ZNode:简约版文件系统和Unix文件系统很类似,整体可以看做一棵树,每个节点成为一个ZNode,每个ZNode能够存储1MB的数据,每个ZNode都可通过其路径唯一标识。
ZNode是一个多层级的节点命名空间。
5.2 ZNode节点类型
ZNode 分为两类:持久节点、临时节点
- 持久节点:持久节点是在create创建后就一直存在的,直到有删除操作清除这个节点,否则不会因为会话失效而消失。
# 创建节点/zk_test,并设置数据my_data
create /zk_test my_data
# 显示的删除永久节点
delete /zk_test
- 临时节点:临时节点的生命周期跟客户端会话session绑定,一旦会话失效,临时节点被删除。
# client1上创建临时节点
create -e /tmp tmpdata
# client2上查看client1创建的临时节点
ls /
# client1断开连接
close
# client2上观察现象,发现临时节点被自动删除
ls /
-
有序节点:ZNode也可以设置为有序节点,分为有序持久节点和有序临时节点
-
创建节点时,可添加一个特殊的属性:SEQUENTIAL。
-
一旦节点被标记上这个属性,那么在这个节点被创建时,ZooKeeper 就会自动在其节点后面追加上一个整型数字
-
这个整数是一个由父节点维护的自增数字。
-
提供了创建唯一名字的ZNode的方式
# 创建持久、有序节点 create -s /test01 test01-data # Created /test010000000009
-
5.3 事务zxid
每次修改 ZooKeeper 状态(增删改的操作)都会收到一个 zxid 形式的时间戳,也就是 ZooKeeper 事务 ID。
事务 ID 是 ZooKeeper 中所有修改总的次序。每个修改都有唯一的 zxid,如果 zxid1 小于 zxid2,那么 zxid1 在 zxid2 之前发生。
实际上,ZooKeeper的每个节点维护着两个Zxid值,为别为:cZxid、mZxid。
-
(1)cZxid: 是节点的创建时间所对应的Zxid格式时间戳。
-
(2)mZxid:是节点的修改时间所对应的Zxid格式时间戳。
ZXID通常是一个64位的数字。由epoch+counter组成,前32位epoch代表Leader关系是否改变,每次一个Leader被选出来,它都会有一个新的epoch。后32位是递增计数。
5.4 Watcher监听器
5.4.1 定义
如果采用轮询方式向Zookeeper Server访问,那么效率比较低,代价也大,因此使用基于通知的机制:客户端在znode上注册一个Watcher监视器,当znode上数据出现变化,watcher监测到此变化,就通知客户端。
Watcher就是Zookeeper客户端在服务器上注册的事件监听器。
可以用于监听znode上的某些事件,比如znode数据修改、节点增删等;当监听到事件后,watcher会触发通知客户端**。
5.4.2 如何设置Watcher
注意:Watcher是一个单次触发的操作
可以设置watcher的命令如下:
使用示例:
- 首先要创建节点,这样位于Zookeeper集群的所有机器都能看到
#ls path [watch]
#node-01 上执行
ls /zk_test watch
#node-02 上执行
#创建节点
create /zk_test/dir01 dir01-data
#观察node-01上变化
[zk: node-01:2181,node-02:2181,node-03:2181(CONNECTED) 87]
WATCHER::
WatchedEvent state:SyncConnected type:NodeChildrenChanged path:/zk_test
- 监控节点数据的变化
#监控节点数据的变化;
#node2上。
#获得节点各种数据
get /zk_test watch
#node3上
#设置节点数据
set /zk_test "junk01"
#观察node2上cli的输出,检测到变化
-
节点上下线监控
- 这种模式也是Hadoop HA中使用到的方式,活跃的namenode就是通过临时节点来被zk监听。
- 原理:在一个节点服务器上设置临时节点并对该节点注册监听器,如果该节点挂了,那么代表会话结束导致临时节点也会被删除,这样监听器就会发送消息,通知其他节点。
- 用到的特性:Zookeeper + 临时节点
- 实现:
#client1操作 # 创建临时节点 create -e /zk_tmp tmp-data #client2操作 # 在/zk_tmp注册监听器 ls /zk_tmp watch #回到client1操作 # 模拟节点下线 close #观察client2 WATCHER:: WatchedEvent state:SyncConnected type:NodeDeleted path:/zk_tmp
六、Zookeeper工作原理
ZooKeeper使用原子广播协议叫做Zab(ZooKeeper Automic Broadcast)协议
Zab协议有两种模式,它们分别是恢复模式(选主)和广播模式(同步)
6.1 写数据流程
- 1.Client向Zookeeper中第一个Server发出一个写数据的请求
- 2.如果Server1不是leader,那么它会把接受到的请求进一步发送给leader,leader再将写请求广播给其他Server。
- 3.各个Server写成功后通知回leader,当leader收到大多数(一般是超过半数)Server写成功后,就认为数据写成功了。
- 4.写成功后,leader再告诉Server1已经成功,Server1再反馈给Client该写数据请求已经完成。
6.2 Zookeeper状态同步
同步机制应该是保证Zookeeper全局数据一致。
完成leader选举后,zk就进入ZooKeeper之间状态同步过程:
-
1.leader等待server连接;
-
2.Follower连接leader,将最大的zxid发送给leader;
-
3.Leader根据follower的zxid确定同步点, 如果此时有某个Follower节点的数据不是最新,就会同步该Follewer的最大zxid之后的内容;
-
4.完成同步后通知follower 已经成为update状态;
-
5.Follower收到uptodate消息后,又可以重新接受client的请求进行服务了。
6.3 读数据流程
当Client向zookeeper发出读请求时,无论是Leader还是Follower,都直接返回查询结果,至于数据是否一致,暂时还没找到验证。
上一篇: Flink快速入门
下一篇: 浅谈ThreadLocal类