Zookeeper
1. 概念
zookeeper主要是文件系统和通知机制
文件系统主要是用来存储数据
通知机制主要是服务器或者客户端进行通知,并且监督
基于观察者模式设计的分布式服务管理框架,开源的分布式框架
特点
一个leader,多个follower的集群
集群只要有半数以上包括半数就可正常服务,一般安装奇数台服务器
全局数据一致,每个服务器都保存同样的数据,实时更新
更新的请求顺序保持顺序(来自同一个服务器)
数据更新的原子性,数据要么成功要么失败
数据实时更新性很快
主要的集群步骤为
服务端启动时去注册信息(创建都是临时节点)
获取到当前在线服务器列表,并且注册监听
服务器节点下线
服务器节点上下线事件通知
process(){重新再去获取服务器列表,并注册监听}
数据结构
与 Unix 文件系统很类似,可看成树形结构,每个节点称做一个 ZNode。每一个 ZNode 默认能够存储 1MB 的数据。也就是只能存储小数据
应用场景
统一命名服务(域名服务)
统一配置管理(一个集群中的所有配置都一致,且也要实时更新同步)
将配置信息写入ZooKeeper上的一个Znode,各个客户端服务器监听这个Znode。一旦Znode中的数据被修改,ZooKeeper将通知各个客户端服务器
统一集群管理(掌握实时状态)
将节点信息写入ZooKeeper上的一个ZNode。监听ZNode获取实时状态变化
服务器节点动态上下线
软负载均衡(根据每个节点的访问数,让访问数最少的服务器处理最新的数据需求)
2. 安装
bin目录 框架启动停止,客户端和服务端的
conf 配置文件信息
docs文档
lib 配置文档的依赖
2.1 配置文件解读
配置文件的5大参数
tickTime = 2000发送时间
initLimit = 10初始化通信的时间,最多不能超过的时间,超过的话,通信失败
syncLimit = 5建立好连接后,下次的通信时间如果超过,通信失败
dataDir保存zookeeper的数据,默认是temp会被系统定期清除clientPort = 2181客户端的连接端口,一般不需要修改
3. zookeeper集群操作
3.1 集群安装
3.1.1 集群安装
比如三个节点部署zookeeper
那么需要3台服务器
在每台服务器中解压zookeeper压缩包并修改其配置文件等
区别在于下面
要在创建保存zookeeper的数据文件夹中
新建一个myid文件(和源码的myid对应上)相当于服务器的唯一编号
具体编号是多少,对应该服务器是哪一台服务器编号
之后将其终端执行xsync /opt/apache-zookeeper-3.5.7-bin,主要功能是同步发脚本
在conf的配置文件zoo.cfg末尾添加如下配置
标识服务器的编号
server.2=hadoop102:2888:3888
server.3=hadoop103:2888:3888
server.4=hadoop104:2888:3888
当前主要配置编号的参数是server.A=B:C:D
A标识第几台服务器
B标识服务器地址
C标识服务器 Follower 与集群中的 Leader 服务器交换信息的端口
D主要是选举,如果Leader 服务器挂了。这个端口就是用来执行选举时服务器相互通信的端口,通过这个端口进行重新选举leader
配置文件以及服务器编号设置好后即可启动
3.1.2 选举机制
以下主要是面试的重点
区分好第一次启动与非第一次启动的步骤
epoch在没有leader的时候,主要是逻辑时钟(与数字电路中的逻辑时钟相同)
非第一次启动,在没有leader的时候
其判断依据是:epoch任职期>事务id>服务器sid
3.1.3 集群启动停止脚本
如果服务器太多,需要一台一台启动和关闭会比较麻烦
脚本文件大致如下:
建立一个后缀名为sh的文件
#!/bin/bash
case $1 in
"start"){
for i in hadoop102 hadoop103 hadoop104
do
echo ---------- zookeeper $i 启动 ------------
ssh $i "/opt/module/zookeeper-3.5.7/bin/zkServer.sh
start"
done
};;
"stop"){
for i in hadoop102 hadoop103 hadoop104
do
echo ---------- zookeeper $i 停止 ------------
ssh $i "/opt/module/zookeeper-3.5.7/bin/zkServer.sh
stop"
done
};;
"status"){
for i in hadoop102 hadoop103 hadoop104
do
echo ---------- zookeeper $i 状态 ------------
ssh $i "/opt/module/zookeeper-3.5.7/bin/zkServer.sh
status"
done
};;
esac
修改该文件的权限
通过chmod 777 zk.sh
之后在一个服务器中执行./zk.sh start就可启动脚本文件,其他命令也如此
查看其进程号
可以通过jpsall 就可查看所有服务器的进程
3.2 客户端命令
3.2.1 常用命令
命令 功能
help 显示所有操作命令
ls path 使用 ls 命令来查看当前 znode 的子节点 [可监听] ,-w 监听子节点变化,-s 附加次级信息
create 普通创建
-s 含有序列
-e 临时(重启或者超时消失)
get path 获得节点的值 [可监听] ,-w 监听节点内容变化
-s 附加次级信息
set 设置节点的具体值
stat 查看节点状态
delete 删除节点
deleteall 递归删除节点
通过ls /
查看当前znode包含的信息
[zk: localhost:2181(CONNECTED) 0] ls /
[zookeeper]
查看当前数据节点详细信息ls -s /
名称 表述
czxid 创建节点的事务 zxid,每次修改 ZooKeeper 状态都会产生一个 ZooKeeper 事务 ID。事务 ID 是 ZooKeeper 中所有修改总的次序。每次修改都有唯一的 zxid,如果 zxid1 小于 zxid2,那么 zxid1 在 zxid2 之前发生。
ctime znode 被创建的毫秒数(从 1970 年开始)
mzxid znode 最后更新的事务 zxid
mtime znode 最后修改的毫秒数(从 1970 年开始)
pZxid znode 最后更新的子节点 zxid
cversion znode 子节点变化号,znode 子节点修改次数
dataversion znode 数据变化号
aclVersion znode 访问控制列表的变化号
ephemeralOwner 如果是临时节点,这个是 znode 拥有者的 session id。如果不是临时节点则是 0
dataLength znode 的数据长度
numChildren znode 子节点数量
如果要启动专门的服务器
启动客户端的时候后缀要加上./zkCli.sh -server 服务器名:2181
博主的启动方式也为./zkCli.sh -server hadoop102:2181
3.2.2 节点类型
节点类型分为(两两进行组合)
- 持久/短暂
- 有序号/无序号
创建节点不带序号的
通过create 进行创建
如果创建节点带序号的通过加上-s参数
即便创建两个一样的,-s 自动会加上序号辨别两者不同
退出客户端之后,这些节点并没有被清除
如果创建临时节点 加上参数 -e
临时节点不带序号-e
临时节点带序号-e -s
如果退出客户端,这些短暂节点将会被清除
如果修改节点的值,通过set key value即可
3.2.3 监听器原理
注册一次监听一次
主要有节点值的变化还有节点路径的变化
在一个服务器中通过修改其值或者路径
在另一个服务器中进行监听,通过get -w 节点值进行监听节点值的变化
如果是路径的变化,在另一个服务器中进行ls -w 路径进行监听
如果有很多个节点
不能直接使用删除delete
需要删除deleteall
查看节点的状态信息 stat
3.3 客户端代码操作
创建一个工程
在pom文件中配好依赖文件
junit
junit
RELEASE
org.apache.logging.log4j
log4j-core
2.8.2
org.apache.zookeeper
zookeeper
3.5.7
log4j.rootLogger=INFO, stdout
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%d %p [%c]
- %m%n
log4j.appender.logfile=org.apache.log4j.FileAppender
log4j.appender.logfile.File=target/spring.log
log4j.appender.logfile.layout=org.apache.log4j.PatternLayout
log4j.appender.logfile.layout.ConversionPattern=%d %p [%c]
- %m%n
- 在客户端中生成具体代码
初始化并且负责监听节点
主要通过设置连接的服务器,以及超时参数,监听匿名函数new Watcher() {}
// 注意:逗号左右不能有空格
private String connectString = "hadoop102:2181,hadoop103:2181,hadoop104:2181";
private int sessionTimeout = 2000;
private ZooKeeper zkClient;
@Before
public void init() throws IOException {
zkClient = new ZooKeeper(connectString, sessionTimeout, new Watcher() {
@Override
public void process(WatchedEvent watchedEvent) {
});
}
}
创建一个新的节点
创建的节点必须在初始化节点这些之后,通过注解,在之前中加入before
- 第一个参数是路径
- 第二个参数是数据,要求是字节类型,需要用
getBytes() - 第三个参数是权限,
Ids.OPEN_ACL_UNSAFE允许所有人进行访问 - 第四个参数是创建节点的类型
@Test
public void create() throws KeeperException, InterruptedException {
String nodeCreated = zkClient.create("/manongyanjiuseng ", "123".getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);
获取子节点并且监听其变化
获取子节点通过getChildren()
关于获取子节点一共有两种
获取子节点第一个是路径,第二个是监听函数,如果使用true,则会使用初始化函数中重写的监听函数
@Test
public void getChildren() throws KeeperException, InterruptedException {
List children = zkClient.getChildren("/", true);
for (String child : children) {
System.out.println(child);
}
// 延时
Thread.sleep(Long.MAX_VALUE);
}
注册一次生效一次,还需要在进行注册
希望通过延迟函数延迟程序的结束,继续监听Thread.sleep(Long.MAX_VALUE);
放在监听函数中就可以注册一次生效一次,之后在注册
zkClient = new ZooKeeper(connectString, sessionTimeout, new Watcher() {
@Override
public void process(WatchedEvent watchedEvent) {
System.out.println("-------------------------------");
List children = null;
try {
children = zkClient.getChildren("/", true);
for (String child : children) {
System.out.println(child);
}
System.out.println("-------------------------------");
} catch (KeeperException e) {
e.printStackTrace();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
判断节点是否存在
exits函数,返回的是状态信息,通过状态信息判断是否还在
第一个参数是路径
第二个参数是是否监听
@Test
public void exist() throws KeeperException, InterruptedException {
Stat stat = zkClient.exists("/manongyanjiuseng", false);
System.out.println(stat==null? "not exist " : "exist");
}
3.4 客户端向服务端写数据流程
发送给leader的时候
通俗解释:客户端给服务器的leader发送写请求,写完数据后给手下发送写请求,手下写完发送给leader,超过半票以上都写了则发回给客户端。之后leader在给其他手下让他们写,写完在发数据给leader
发送给follower的时候
通俗解释:客户端给手下发送写的请求,手下给leader发送写的请求,写完后,给手下发送写的请求,手下写完后给leader发送确认,超过半票,leader确认后,发给刻划断,之后leader在发送写请求给其他手下