本文均总结自B站,有些图片是在视频上截取的。
B站id:BV1PW411r7iP
ZooKeeper
1.入门
1.1概述
ZooKeeper是一个开源的分布式的,为分布式提供协调服务的Apache项目。
从设计模式角度来理解:是一个基于观察者模式的分布式管理服务器框架,它负责存储和管理大家都关心的数据,然后接收观察者的注册,一旦这些数据的状态发生变化,ZooKeeper就将负责通知已经在ZooKeeper上注册的那些观察者做出相应的反应。
1.2特点
- 一个领导者,多个跟随者组成的集群。
- 集群中只要有半数以上的节点存活,ZooKeeper集群就能正常服务。
- 全局数据一致:每个Server保存一分相同的数据副本,Client无论连接到哪个Server,数据都是一致的。
- 更新请求顺序,来自同一个Client的更新请求按其发送顺序依次执行。
- 数据更新的原子性,一次数据更新要么成功,要么失败(与事务相同意义)。
- 实时性,在一定时间范围内,Client能读到最新数据。
1.3数据结构
ZooKeeper的数据模型结构于Unix文件系统很类似,整体上可以看做是一棵树,每个节点称作一个ZNode。每个ZNode默认能够存储1MB的数据,每个ZNde都可以通过其路径唯一标识。
1.4 应用场景
- 在分布式环境下,经常需要对应用/服务进行统一命名,便于识别。
- 配置文件同步,将配置文件写入ZooKeeper上的ZNode。各个客户端服务器监听这个ZNode。一旦这个ZNode中的数据被修改,ZooKeeper将通知各个客户端服务器。
- ZooKeeper可以实时监听控制节点的变化,将节点信息写入ZNode,监听这个ZNode获取它的实时变化。
- 可以实时洞察到服务器上下线的变化。
- 负载均衡,ZooKeeper记录着每台服务器的访问数,让访问最少的服务器去处理最新的客户端请求。
1.5下载地址
2.ZooKeeper安装
在安装前需要安装好JDK。
2.1 上传&解压
将下载完成的ZooKeeper上传到Linux并且解压。
2.2修改配置文件
- 将conf文件夹下的zoo_sample.cfg重命名为zoo.cfg
mv zoo_sample.cfg zoo.cfg
- 修改zoo.fig文件中的dataDir路径
vim zoo.fig
改为:
dataDir=自己的路径/zkData
- 在zookeeper文件下新建zkData文件
mkdir zkData
2.3启动
- 启动服务器(zookeeper文件下)
bin/zkServer.sh start
2. 关闭服务器(zookeeper文件下)
bin/zkServer.sh stop
- 启动客户端(zookeeper文件下)
bin/zkServer.sh
4. 退出客户端
quit
2.4 zoo.cfg参数解读
2.5 分布式安装
安装步骤跟上方安装步骤一。
- 需要在zkData文件下创建myid文件
touch myid
- 编辑myid
vim myid
- 在myid中添加服务号(id唯一标识)
2
- 在每个服务器中都添加
- 分别启动每台服务器的ZooKeeper
查看状态以及是leader或者follower
bin/zkServer.sh status
2.6 客户端操作语言
- 启动客户端
/bin zkCli.sh
- 查看所有操作命令
help
- 查看zNode中的详情
ls /
- 查看节点详细数据
ls2 /
- 创建普通节点
create /demo "value"
create /path1/path2 "value"
查看zNode
6. 获取节点的值
get /path
- 创建短暂节点
当前客户端与服务器Server断开了连接就会删除。
create -e /path "value"
- 创建带有序号的节点
create -s /path "value"
- 修改节点值
set /path "new Value"
- 监听节点数据变化
get /path watch
- 监听子节点数量的变化
ls /path watch
- 删除节点
delete /path
- 递归删除节点
如果一个zNode下有子节点,就没法需要使用rmr删除这个zNode
rmr /path
- 查看节点状态
stat /path
3. 内部原理
3.1选举机制
- 半数机制:ZooKeeper集群中,半数以上节点存活,集群可用,所以ZooKeeper适合安装奇数服务器。
- ZooKeeper虽然在配置文件中并没有指定Master和Slave。但是,Zookeeper工作时,只有一个节点为Leader,其它的都是Follower,Leader是通过内部的选举机制临时产生的。如果有五台服务器一次叠加,则第三个为Follower(Master),无论后面添加的服务器性能再优。
3.2 节点类型
序号可以被用于为所有的时间进行全部排序,这样客户端可以通过顺序号推断事件的顺序。
3.2.1持久
- 持久化目录节点:客户端与服务器断开连接后,创建的节点不删除。
- 持久化顺序编号目录节点:客户端与ZooKeeper断开连接后,该节点依旧存在,只是ZooKeeper给该节点名称进行顺序编号。
3.2.2短暂
- 客户端与服务器断开连接后,创建的节点自己删除。
- 临时顺序编号目录节点:客户端与ZooKeeper断开连接后,该节点被删除,只是ZooKeeper给该节点名称进行顺序编号。
3.3Stat结构体
图片来自B站截图
3.4监听器原理
3.5写数据流程
4.API
4.1 Maven依赖
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.13</version>
</dependency>
<dependency>
<groupId>org.apache.zookeeper</groupId>
<artifactId>zookeeper</artifactId>
<version>3.4.9</version>
</dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-core</artifactId>
<version>2.8.2</version>
</dependency>
</dependencies>
4.2 建立连接
//ZooKeeper连接信息(集群用逗号隔开)
private String conn="your port:2181";
// 超时时间 单位毫秒
private int sessionTimeout = 2000;
private ZooKeeper zkClick;
@Test
public void init() throws IOException {
zkClick = new ZooKeeper(conn,sessionTimeout, new Watcher() {
public void process(WatchedEvent watchedEvent) {
});
System.out.println("连接状态:"+zkClick.getState());
}
当连接状态为Connecting时就成功了,不过我看别人博客有写是Connected才行,具体原因没找到,
反正我的Connecting可以使用。
4.3 创建节点
@Test
public void testCreateNode() throws KeeperException, InterruptedException {
//参数分别是:路径,value,权限,短暂/持久(普通)/短暂有序/持久(普通)有序
String s = zkClick.create("/javaNode", "haoshuaio".getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);
System.out.println(s);
}
4.4 监听子节点的变化
public class zookeeper {
//ZooKeeper连接信息(集群用逗号隔开)
private String conn="your port:2181";
// 超时时间 单位毫秒
private int sessionTimeout = 2000;
private ZooKeeper zkClick;
@Before
public void init() throws IOException {
zkClick = new ZooKeeper(conn,sessionTimeout, new Watcher() {
public void process(WatchedEvent watchedEvent) {
List<String> children;
try {
children = zkClick.getChildren("/",true);
for (String child : children) {
System.out.println(child);
}
System.out.println("--------------------");
} catch (KeeperException e) {
e.printStackTrace();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
System.out.println("连接状态:"+zkClick.getState());
}
/**
* 获取子节点并监听变化
* @throws KeeperException
* @throws InterruptedException
*/
@Test
public void getDataAndWatch() throws KeeperException, InterruptedException {
// 获取目录下的子节点,并选择是否监听
List<String> children = zkClick.getChildren("/", true);
for (String child : children) {
System.out.println(child);
}
Thread.sleep(Long.MAX_VALUE);
}
4.5 判断Znode是否存在
/**
* 判断Znode是否存在
* @throws KeeperException
* @throws InterruptedException
*/
@Test
public void exist() throws KeeperException, InterruptedException {
Stat stat= zkClick.exists("/demo0421", false);
System.out.println(stat==null ? "没有" : "有");
}