Zookeeper框架基础笔记

视频教程传送门 -> https://www.bilibili.com/video/BV19b411772h

1. Zookeeper

Zookeeper是一个分布式协调服务的开源框架。
Zookeeper本质是一个分布式的小文件存储系统。

@Zookeeper特性
1)全局数据一致:每个服务器保存一份相同的数据副本,客户端无论连接哪个服务器展示的数据是一致的
2)可靠性:消息(即增删改查)被一台服务器接受,那么也将被所有服务器接受
3)顺序性:全局有序 -- 在一台服务器上,如果消息a在消息b前发布,则在所有服务器上都是如此
                    偏序 -- 消息b在消息a后被同一个发送着发布,a必将排在b前面
4)数据更新原子性:一次数据更新要么成功(半数以上节点成功),要么失败
5)实时性:Zookeeper保证客户端将在一个时间间隔范围内获得服务器的更新信息,或者服务器失效的信息

ZooKeeper具备CP特性
任何时刻的访问请求能得到一致的数据结果
系统对网络分割具备容错性
不保证每次服务请求的可用性

@Zookeeper集群角色

Leader
Zookeeper集群工作的核心
事务请求(写操作)的唯一调度和处理者,保证集群事务处理的顺序性
集群内部各个服务器的调度者

说明:对于create、setData、delete等有写操作的请求,需要统一转发给Leader处理,Leader需要决定编号、执行操作,这个过程成为一个事务。

Follower
处理客户端非事务(读操作)请求,转发事务请求给Leader
参与集群Leader选举投票

Observer   针对访问量比较大的Zookeeper集群,可以增加观察者角色(横向扩展)
提供非事务服务、不参加投票

@Zookeeper集群搭建
Zookeeper集群通常由2n+1台服务器组成(Leader选举基于Paxos算法实现)
1)Leader+Follower模式
- 配置主机名称和IP地址映射
- 修改Zookeeper配置文件
- 远程复制分发安装文件
- 设置myid
- 启动Zookeeper集群

2)启用Observer模式
可在对应节点的配置文件添加 peerType=observer
并且在配置文件指定哪些节点被指定为Observer,如
server.1:localhost:2181:3181:observer

以3台服务器为例,搭建步骤如下

step1 
确认环境已安装jdk
检查集群时间是否同步
检查防火墙是否关闭(生产环境配置防火墙规则)
检查是否配置主机IP映射

step2
tar -xzvf zookeeper-xxx.tar.gz
mv zookeeper-xxx zookeeper

step3
修改环境变量(3台Zookeeper都要修改)
vi /etc/profile 添加如下行
export ZOOKEEPER_HOME=/home/hadoop/zookeeper
export PATH=$PATH:$ZOOKEEPER_HOME/bin
source /etc/profile

step4
修改Zookeeper配置文件(先在一台修改)
cd zookeeper/conf
cp zoo_sample.cfg zoo.cfg
vi zoo.cfg 添加如下行
#可以修改数据路径
dataDir=/root/apps/zookeeper/zkdata
#两个端口分别是 心跳端口、选举端口
server.1=node-1:2888:3888
server.2=node-2:2888:3888
server.3=node-3:2888:3888

创建文件myid
cd /root/apps/zookeeper/zkdata
echo 1 > myid

step5 
分发安装包到其它服务器
scp -r /root/apps root@node-1:/
scp -r /root/apps root@node-2:/

step6
修改其它服务器的myid文件
服务器node-1 修改myid内容为2
服务器node-2 修改myid内容为3

step7
在每台服务器启动zookeeper
cd bin
./zkServer.sh start

查看状态(还可以看到是leader还是follower)
./zkServer.sh status

 

2. Zookeeper数据模型

@Znode特点
类似于文件系统的目录树,但也有的不同之处,其特点如下
1)Znode兼具文件和目录两种特点
   - 既像文件一样维护数据、元信息、ACL、时间戳等数据结构
   - 又像目录一样可以作为路径标识的一部分,且可以具有子Znode
   用户可以对Znode具有增删改查

2)Znode具有原子性操作
   - 读操作将获取与节点相关的所有数据
   - 写操作将替换掉节点的所有数据
每一个节点都拥有自己的ACL(访问控制列表),这个列表限定了特定用户对目标节点可以执行的操作

3)Znode存储数据大小有限制
Zookeeper虽然可以关联一些数据,但并没有被设计为常规的数据库或者大数据存储
相反,它用来管理调度数据,如分布式应用中的配置文件信息、状态信息、汇集位置等(都是很小的数据,KB级)
Zookeeper的服务器和客户端都被设计为严格检查并限制每个Znode的数据大小至多1M

4)Znode通过路径引用 
路径必须是绝对路径、且是唯一的

@Znode组成
每个Znode由3部分组成
stat 状态信息,描述Znode版本、权限等信息
data 与该Znode关联的数据
children 该Znode下的子节点

@Znode节点类型
临时节点&永久节点
临时节点:该节点的生命周期依赖于创建它们的会话
                  会话结束临时节点将被自动删除,也可以手动删除
                  临时节点不允许拥有子节点
永久节点:该节点的生命周期不依赖于会话,执行删除操作才会被删除

Znode的序列特性
如果创建的时候指定的话,该Znode的名字后面会自动追加一个不断增加的序列号
序列号记录每个子节点创建的先后顺序,对于此节点的父节点来说是唯一的
序列号的格式为"%10d"(10位数字,没有数值的数位用0补充)

@Znode属性
通过命令get,可以获得节点的属性

dataVersion:数据版本号,每次对节点进行set操作,dataVersion的值都会增加1(即使设置的是相同数据)
                      可以有效避免数据更新时出现的先后顺序问题
cversion:子节点的版本号,当Znode的子节点发生变化时,cversion的值就会加1
aclVersion:ACL的版本号
cZxid:Znode创建的事务id
mZxid:Znode被修改的事务id,每次对Znode的修改都会更新该值
             对于zk来说,每次的变化都会产生一个唯一的事务id,zxid(Zookeeper Transaction  Id)
             通过zxid可以确定更新操作的先后顺序。例如,如果zxid1小于zxid2,说明zxid1操作先于zxid2发生
             zxid对于整个zk都是唯一的,即使操作的是不同的znode
ctime:节点创建时的时间戳
mtime:节点最新一次更新发生时的时间戳
ephemeralOwner:如果该节点位临时节点,ephemeralOwner值表示与该节点绑定的session id;反之,ephemeralOwner为0
在client和server通信之前,首先需要建立连接(session),连接建立后,如果发生连接超时、授权失败、显式关闭连接,连接处于CLOSED状态,此时session结束

3. Zookeeper shell

@客户端连接
运行 zkCli.sh -server ip 进入命令行工具
输入help,输出zk shell提示
说明:-server可选,不带会在本机查找zk,带上连接到远端zk

@shell基本操作
1)创建节点
create [-s] [-e] path data acl
-s指定创建顺序节点
-e指定创建临时节点
acl用来进行权限控制

2)读取节点
ls 命令   列出Zookeeper指定节点下的所有子节点(只能查看第一级子节点)
get 命令  获取指定Znode的数据内容和属性信息
ls2 命令   列出的信息比ls详细,包括子节点和属性信息,但不包括数据内容

ls path [watch]
get path [watch]
ls2 path [watch]

3)更新节点
set path data [version]
data 更新的新内容
version 数据的版本
更新后dataVersion会递增

4)删除节点
delete path [version]
若删除的节点存在子节点则无法删除
可以先删除子节点
或者使用 rmr path 递归删除节点

5)quata
setquota -n|-b val path 对节点增加限制
-n 表示子节点的最大个数
-b 表示数据值的最大长度
val 子节点的最大个数或数据值的最大长度
path 节点路径

listquota path 列出指定节点的quota

delquota [-n|-b] path 删除quota

注意:即使节点数超了,仍能创建,会在日志zookeeper.out中打印WARN

6)history列出命令历史
redo重新执行指定命令编号的历史命令

4. Zookeeper Watcher

Zookeeper提供了分布式数据发布/订阅功能
一对多订阅=> 能让多个订阅者同时监听某一个主题对象
当主题对象自身状态变化时,会通知所有订阅者

Zookeeper引入Watcher机制来实现分布式通知功能
Zookeeper允许客户端向服务器注册一个Watcher监听,当服务端的一些事件触发了这个Watcher,就会向指定的客户端发送一个事件来通知
触发事件的种类有节点创建、节点删除、节点改变、子节点改变等

@Watcher机制过程
客户端向服务端注册Watcher
服务端事件发生触发Watcher
客户端回调Watcher得到触发事件情况

@Watcher机制特点
1)一次性触发
事件发生触发监听,一个watcher event就会发送到设置监听的客户端
这种效果式一次性的,再发生同样的事件不会触发

2)事件封装
Zookeeper使用WatchedEvent对象来封装服务端事件并传递
WatchedEvent包含了每一个事件的三个基本属性:
通知状态(keeperState)
事件类型(eventType)
节点路径(path)

3)event异步发送
Watcher的通知事件从服务端发送到客户端是异步的

4)先注册再触发
Zookeeper中的Watcher机制必须客户端先去服务端注册监听

同一个事件类型在不同的通知状态中代表的含义不同,举例如下表

其中连接状态事件(type=None,path=null)不需要客户端注册,客户端只要有需要直接处理就行了
 

@Shell客户端设置Watcher
设置节点数据变动监听
【例】get /aaa0000000001 watch

通过另一个客户端更改节点数据
set /aaa0000000001 456789

此时设置监听的节点收到的通知

再次通过另一个客户端更改节点数据,设置监听的节点不会收到通知

5. Zookeeper选举机制

默认算法是FastLeaderElection,采用投票数大于半数则胜出的逻辑。

@相关概念
服务器ID
例如有三台服务器,编号分别为1、2、3
编号越大在选举算法中的权重越大

选举状态
LOOKING    竞选状态
FOLLOWING  随从状态,同步leader状态,参与投票
OBSERVING  观察状态,同步leader状态,不参与投票
LEADING    领导者状态

数据ID
服务器中存放的最新数据version
值越大说明数据越新,在选举算法中数据越新权重越大

逻辑时钟/投票次数
同一轮投票过程中的逻辑时钟值是相同的
每投完一次票这个数据会增加

@全新集群选举
假设目前有5台服务器,每台服务器均没有数据
编号分别是1、2、3、4、5,按编号依次启动

规则:每个机器都给自己投票、投票数过半选举结束

step1 服务器1启动,给自己投票
然后发投票信息,由于其它机器还没有启动所以它收不到反馈信息
服务器1处于Looking状态

step2 服务器2启动,给自己投票
同时与服务器1交换信息
由于服务器2的编号大所以胜出
但此时投票数没有大于半数,服务器1、2处于Looking状态

step3 服务器3启动,给自己投票
同时与服务器1、2交换信息
由于服务器2的编号大所以胜出
此时投票数正好大于半数,投票结束,服务器3成为Leader,服务器1、2成为Follower

step4 服务器4启动,给自己投票
同时与服务器1、2、3交换信息
投票已结束,服务器4成为Follower

step5 服务器5启动,同服务器4

全新集群选举主要影响因素 -> 服务器编号

@非全新集群选举
对于运行正常的zookeeper集群,中途有服务器down,需要重新选举时,选举过程就需要加入数据ID、服务器ID和逻辑时钟

数据ID:数据新的version就大,数据每次更新都会更新version
服务器ID:配置的myid中的值,每个机器一个
逻辑时钟:这个值从0开始递增,每次选举对应一个值。如果在同一次选举中,这个值是一致的

选举Leader标准如下
1)逻辑时钟小的选举结果被忽略,重新投票
2)同一逻辑时钟后,数据id大的胜出
3)数据id相同的情况下,服务器id大的胜出

6. 典型应用

@数据发布与订阅(配置中心)
配置中心=> 发布者将数据发布到ZK节点上,供订阅者动态获取数据,实现配置信息的集中式管理和动态更新

应用在启动的时候会主动来获取一次配置,同时,在节点上注册一个Watcher
配置有更新会实时通知到订阅的客户端,从而达到获取最新配置信息的目的

例如,分布式搜索服务中,索引的元信息和服务器集群节点状态存放在ZK的一些指定节点,供各个客户端订阅使用

注意:适合数据量很小的场景,这样数据更新会比较快

@命名服务(Naming Service)
在分布式系统中,通过使用命名服务,客户端应用能够根据指定名字来获取资源或服务的地址,提供者等信息
被命名的实体通常可以是集群中的机器、提供的服务地址、远程对象等 => 可以统称为名字(Name)
通过调用ZK提供的创建节点的API,可以创建一个全局唯一的path => 可以作为一个名称

例如,阿里巴巴开源的分布式服务框架Dubbo中使用Zookeeper来作为其命名服务,维护全局的服务地址列表

@分布式锁
锁服务可以分为两类,一个是保持独占,另一个是控制时序

1)保持独占
所有试图获取这个锁的客户端,最终只有一个可以成功获得这把锁

通常做法是把zk上的一个Znode看作一把锁,通过create Znode的方式来实现
所有客户端都去创建/distribute_lock节点(是临时节点且非序列化),最终成功创建的那个客户端拥有这把锁

2)控制时序
所有视图来获取这个锁的客户端,最终都会被安排执行,只是有个全局时序

做法基本和前述相同,只是/distribute_lock已经预先存在
客户端在它下面创建临时有序节点
父节点/distribute_lock维持一份sequence,保证子节点创建的时序性

 

推荐阅读:
我们能用zookeeper做什么 https://blog.csdn.net/zhangzq86/article/details/80981234

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章