Zookeeper框架
ZK的概述
1、ZK本身也是一个集群
2、ZK本身也可以存数据(配置数据),可以理解为一个数据库
3、ZK单独使用没有任何意义,ZK用来管理别的框架
4、ZK顶层是Java语言
5、ZK集群允许部分主机宕机,ZK集群中最坏情况下,只要有大于一半的主机在工作,集群就能维持运转(过半机制)
ZK的特点
1、全局一致性:在ZK的任何一台主机上对节点进行操作,其他主机都要跟着操作,保持完全同步
2、顺序性;在某一台主机上先后创建了A、B两个节点,则其他主机也必须遵循节点创建的先后顺序
3、原子性:对ZK的节点增加是一个原子操作(所有的主机都必须添加添加该节点,这个过程是不可再分的,只有所以的操作都完成,才算成功,否则就算失败)
4、实时性:对ZK的操作几乎是实时的,不会有太大的延迟
ZK集群角色
-
描述
ZK是一个主从架构,有主节点,也有从节点
-
角色
-
Leader
1、集群的管理者,管理整个集群 2、既能完成非事务(查询,读取)操作,也能完成事务(增删改)操作
-
Follower
1、只能完成非事务操作(查询,读取),如果收到客户端的事务请求,则会转发给Leader 2、当Leader挂掉的时候,Follower投票选举新的Leader
-
Observer
1、只能完成非事务操作(查询,读取),如果收到客户端的事务请求,则会转发给Leader 2、当Leader挂掉的时候,Observer不能投票选举新的Leader(能干活,但是被剥夺政治权利)
-
ZK的集群搭建
-
前提
1、虚拟机上必须安装JDK 2、虚拟机要求奇数台 3台虚拟机 允许挂掉1台 剩下2台 5台虚拟机 允许挂掉2台 剩下3台 100台虚拟机 允许挂掉49台 剩下51台
-
集群搭建方案
服务器IP 主机名 myid的值 (ZK的编号) 192.168.88.161 node1 1 192.168.88.162 node2 2 192.168.88.163 node3 3 -
ZK的搭建
-
ZK的启动和状态查看
zkServer.sh start #启动 zkServer.sh stop #启动 zkServer.sh status #启动
-
ZK的日志
1、ZK的日志文件名是:Zookeeper.out 2、日志文件会自动生成在启动命令所在的目录下
-
ZK的一键启动脚本
-
版本1
#!/bin/bash for i in 1 2 3 do ssh root@node$i "source /etc/profile;zkServer.sh $1" done
-
版本2
#!/bin/bash var="" echo "你要进行什么操作?" PS3="请输入你选择:" select var in "start" "stop" "status" do for i in 1 2 3 do echo “"---------------node$i------------------" ssh root@node$i "source /etc/profile;zkServer.sh $var" done break; done echo "-----------Zookeer集群$var完毕!---------------"
-
ZK的数据模型和客户端操作
-
数据模型
1、ZK内部有一个树形的结构的`目录树 2、访问目录树的节点必须使用绝对路径 3、ZK的节点称为Znode 4、Znode既具有文件特点(存数据),又具有文件夹特点(有子节点) 5、ZK中主要存储配置信息,数量量不大,一般是以K为单位,最多不超过1M
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-6bJgl5e9-1662300077368)(Zookeeper框架.assets/image-20220901221103097.png)]
-
客户端连接ZK集群
#方式1-默认连接本主机的ZK zkClient.sh #quit退出 #方式2-连接指定主机 zkClient -server node1/node2/node3
-
shell命令操作ZK的节点
1:创建普通永久节点 #永久节点永远存在,除非手动删除 create /app1 hello 2: 创建永久顺序节点 #永久顺序节点永远存在,除非手动删除,会自动在节点名字后边加一串数字,该数字表示创建节点的先后顺序 #永久顺序节点的创建命令可以多次执行,因为后边会自动加编号 create -s /app2 world 3:创建临时节点 #临时节点依赖当前的会话(客户端和服务器构建的连接),会话消失,则节点自动消失 create -e /tempnode world 4:创建临时顺序节点 #临时顺序节点依赖当前的会话(客户端和服务器构建的连接),会话消失,则节点自动消失 #临时顺序节点的创建命令可以多次执行,因为后边会自动加编号 create -s -e /tempnode2 aaa 5:创建子节点 create /app1/app1_1 null #永久节点的子节点 create -s /app1/app1_2 null #永久顺序节点的子节点 #注意:临时节点,不能创建子节点 6:获取节点数据 get /app1 get /app1/app1_1 7:修改节点数据 set /app1 hadoop 8:删除节点 delete /app1 删除的节点不能有子节点 rmr /app1 递归删除
ZK节点的属性
1、查看ZK节点属性
get /app1
2、分析节点属性
nulll #节点数据
cZxid = 0x900000009 #节点创建事务ID,和创建的时机有关,该值不变
ctime = Sat Sep 03 13:51:44 CST 2022 #节点创建时间
mZxid = 0x900000009 #节点的修改事务ID,每次都节点进行修改,该值加1
mtime = Sat Sep 03 13:51:44 CST 2022 #节点修改时间
pZxid = 0x900000009 #子节点的事务ID,子节点发生变化,则会增加
cversion = 0 #子节点的版本号
dataVersion = 0 #节点数据版本,对字节数据修改,则值加1
aclVersion = 0 #节点的权限
ephemeralOwner = 0x0 #永久节点:0x0 临时节点(会话id): 0x182f95799320001
dataLength = 5 #节点数据的长度
numChildren = 0 #子节点的数量
ZK的Watch机制
- 概念
wath机制就是监控一个节点的变化
数据修改
添加操作
删除操作
....
一旦监控到节点发生变化,则会自动触发某个行为(自定义):通知备用节点让他去进行节点创建
ZK的watch机制在命令行终端是一次性的,如果想重复的监听,则必须使用Java代码来完成
KeeperState | EventType | 触发条件 | 说明 |
---|---|---|---|
None | 连接成功 | ||
SyncConnected | NodeCreated | Znode被创建 | 此时处于连接状态 |
SyncConnected | NodeDeleted | Znode被删除 | 此时处于连接状态 |
SyncConnected | NodeDataChanged | Znode数据被改变 | 此时处于连接状态 |
SyncConnected | NodeChildChanged | Znode的子Znode数据被改变 | 此时处于连接状态 |
-
操作
1、在第一个ZK的客户端执行命令 get /app1 watch #回车之后,ZK会自动的监控/app1节点 2、在第二个ZK的客户端执行命令 set /app1 xxx
ZK的Java操作
-
介绍
在这里我们要通过java将之前的shell命令操作全部实现,也就是通过java对Zookeeper节点进行增删改查
-
Java代码
-
pom.xml
<dependencies> <dependency> <groupId>org.apache.curator</groupId> <artifactId>curator-framework</artifactId> <version>2.12.0</version> </dependency> <dependency> <groupId>org.apache.curator</groupId> <artifactId>curator-recipes</artifactId> <version>2.12.0</version> </dependency> <dependency> <groupId>com.google.collections</groupId> <artifactId>google-collections</artifactId> <version>1.0</version> </dependency> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>RELEASE</version> </dependency> <dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-simple</artifactId> <version>1.7.25</version> </dependency> </dependencies>
-
代码
package pack01_zookeeper; import org.apache.curator.RetryPolicy; import org.apache.curator.framework.CuratorFramework; import org.apache.curator.framework.CuratorFrameworkFactory; import org.apache.curator.framework.api.GetDataBuilder; import org.apache.curator.framework.imps.CuratorFrameworkState; import org.apache.curator.framework.recipes.cache.TreeCache; import org.apache.curator.framework.recipes.cache.TreeCacheEvent; import org.apache.curator.framework.recipes.cache.TreeCacheListener; import org.apache.curator.retry.ExponentialBackoffRetry; import org.apache.zookeeper.CreateMode; import org.apache.zookeeper.data.Stat; import org.junit.After; import org.junit.Before; import org.junit.Test; public class Demo1Zookeeper { private CuratorFramework client; @Before public void init(){ //1:定制一个重试策略 /* 3000 : 间隔3秒重试去连接ZK服务器 3 : 最多连接次 */ RetryPolicy retryPolicy = new ExponentialBackoffRetry(3000, 3); //2:获取一个客户端对象 client = CuratorFrameworkFactory.newClient("node1:2181,node2:2181,node3:2181", retryPolicy); //3:开启客户端 client.start(); } @After public void close(){ //5:关闭客户端 client.close(); } //6:watch机制 @Test public void watchDemo() throws Exception { //4:将要监听的节点树存入缓存中 TreeCache treeCache = new TreeCache(client, "/master"); //5:自定义监听 treeCache.getListenable().addListener(new TreeCacheListener() { //childEvent方法是自动调用,只要你的/app1节点有状态变化,则就会自动执行该方法 public void childEvent(CuratorFramework curatorFramework, TreeCacheEvent treeCacheEvent) throws Exception { //因为不管是:增加/删除/修改节点,都会执行该方法,但是具体是因为哪一种才执行该方法,则需要进一步确认 switch (treeCacheEvent.getType()) { case NODE_ADDED: System.out.println("监控到增加节点事件!"); System.out.println("有客户端上线了!"); break; case NODE_REMOVED: System.out.println("监控到节点移除事件!"); System.out.println("主节点挂掉了!"); System.out.println("让备用节点称为新的主节点!"); break; case NODE_UPDATED: System.out.println("监控到节点修改事件!"); //重新读取配置文件 break; case CONNECTION_SUSPENDED: break; case CONNECTION_RECONNECTED: break; case CONNECTION_LOST: break; case INITIALIZED: break; } } }); //开启监听 treeCache.start(); //让程序挂起 while (true); //Thread.sleep(100000000); } //5、删除节点数据- rmr /app1 @Test public void deleteZnode() throws Exception { //client.delete().forPath("/app1"); //该方法不能删除有子节点的znode client.delete().deletingChildrenIfNeeded().forPath("/app1"); //该方法可以删除所有节点 } //4、查询节点属性 - get /app1 //@Test //public void getZnodeAttr() throws Exception { // CuratorFrameworkState state = client.getState(); // state. // //} //3、查询节点数据 - get /app1 @Test public void getZnodeData() throws Exception { byte[] bytes = client.getData().forPath("/app1"); String str = new String(bytes); System.out.println(str); } //2、修改节点数据 - set /app1 xxx @Test public void setZnode() throws Exception { //4:修改节点数据 client.setData().forPath("/app1", "xxx".getBytes()); } //1、创建节点- create /app1 hello @Test public void createZnode() throws Exception { //4:创建节点 //client.create().forPath("/app1"); //情况1:没有数据,没有节点类型-默认创建的是永久节点 //client.create().creatingParentsIfNeeded().forPath("/app1/app1_1"); //情况2:创建多级节点 //client.create().creatingParentsIfNeeded().forPath("/app1/app1_2","hello".getBytes()); //情况3:创建多级节点并携带数据 /* PERSISTENT -- 永久节点 PERSISTENT_SEQUENTIAL -- 永久顺序节点 EPHEMERAL -- 临时节点 EPHEMERAL_SEQUENTIAL(3, true, true); --临时顺序节点 */ client.create().creatingParentsIfNeeded(). //情况3:创建指定类型节点携带数据 withMode(CreateMode.EPHEMERAL).forPath("/app3","hello".getBytes()); Thread.sleep(10000000L); } }
-
ZK的应用场景
1、主机状态监控
通过创建临时节点和会话关系,来判断主机的健康状态
2、发布和订阅
3、分布式锁
4、注册中心
服务提供者 服务消费者
5、分布式数据共享
ZK的选举机制
-
概述
1、场景1: 启动ZK,需要选举Leader node1启动 投自己1票 和其他主机交换投票信息,系统判断投票数是否过半,否 node2启动 投自己1票 和其他主机交换投票信息,系统判断投票数是否过半,是,谁的myid最大,就是Leader node3启动 投自己1票 和其他主机交换投票信息,发现已经有Leader了,直接成为Follower 启动顺序: node3 ndoe2 node1 ----》node3就是leader 2、场景2:ZK运行的过程中,Leader挂掉,需要选举Leader 2.1 当Leader挂掉之后,系统会判断剩余的主机是否过半,是,则开始选举新Leader,否,则直接终止整个集群 2.2 如果剩余的主机过半,则开始选举新Leader a:比较哪台主机的数据最新,如果某台主机的数据最新,则直接当选Leader b:如果所有主机的数据都是一样新的,则谁的myid最大,谁就是Leader