Zookeeper概念
- Zookeeper是一个分布式、开源的分布式应用程序的协调服务。
- Zookeeper是一个树形目录服务,数据模型与Unix的文件系统目录树类似。
- 主要功能包括:配置管理、分布式锁、集群管理。
ZooKeeper命令操作
ZooKeeper数据模型
- ZK是一个属性目录服务,数据模型与Unix的文件系统目录树类似,拥有一个层次化结构
- 每一个节点被称为ZNode,每个节点上保存自己的数据和节点信息。
- 节点可以拥有子节点,同时允许少量(1MB)数据存储在该节点之下。
- 节点可以分为四大类
1.PERSISTENT:持久化节点
2.EPHEMERAL:临时节点 -e
3.PERSISTENT_SEQUENTIAL:持久化顺序节点 -s
4.EPHEMERAL_SEQUENTIAL:临时顺序节点 -es
ZooKeeper服务端常用命令
启动ZK服务:./zkServer.sh start
查看ZK服务状态:./zkServer.sh status
停止ZK服务:./zkServer.sh stop
重启zk服务:./zkServer.sh restart
ZooKeeper客户端操作
连接ZK服务端:./zkCli.sh -server -ip:port
./zkCli.sh -server 127.0.0.1:2181
断开连接:quit
查看帮助:help
显示指定目录下节点:ls 目录
创建节点:create /节点path value
获取节点值:get /节点path
设置节点值:set /节点path value
删除单个节点:delete /节点path
删除带有子节点的节点:deleteall /节点path
创建临时节点:create -e /节点path value
创建顺序节点:create -s /节点path value
查询节点详细信息:ls -s /节点path
Curator
开启连接
/**
* @author HigginCui
* @date 2023/2/1 下午11:51
*/
public class CuratorTest {
@Test
public void testConnect(){
//重试策略配置
RetryPolicy retryPolicy = new ExponentialBackoffRetry(3000,10);
/**
* @param connectString list of servers to connect to
* @param sessionTimeoutMs session timeout 会话超时时间,单位ms
* @param connectionTimeoutMs connection timeout 链接超时时间,单位ms
* @param retryPolicy retry policy to use
*/
CuratorFramework client = CuratorFrameworkFactory.newClient("127.0.0.1:2181", 60 * 1000, 15 * 1000, retryPolicy);
//开始连接
client.start();
}
@Test
public void testConnect2(){
//重试策略配置
RetryPolicy retryPolicy = new ExponentialBackoffRetry(3000,10);
CuratorFramework client = CuratorFrameworkFactory.builder()
.connectString("127.0.0.1:2181")
.sessionTimeoutMs(60*1000)
.connectionTimeoutMs(15*1000)
.namespace("myRootPath")
.retryPolicy(retryPolicy)
.build();
//开始连接
client.start();
}
}
创建节点
创建基础节点
public class CuratorTest {
CuratorFramework client;
/**
* 创建连接
*/
@Before
public void testBefore(){
//重试策略配置
RetryPolicy retryPolicy = new ExponentialBackoffRetry(3000,10);
client = CuratorFrameworkFactory.builder()
.connectString("127.0.0.1:2181")
.sessionTimeoutMs(60*1000)
.connectionTimeoutMs(15*1000)
.namespace("myRootPath")
.retryPolicy(retryPolicy)
.build();
//开始连接
client.start();
}
/**
* 测试简单的创建节点
* @throws Exception
*/
@Test
public void testCreate() throws Exception{
//创建一个简单的节点
String path = client.create().forPath("/app1");
System.out.println("path===="+path);
}
/**
* 关闭连接
*/
@After
public void close(){
if(null!=client){
client.close();
}
}
}
注:如果没有给节点path设置值,默认为调用客户端的IP
创建节点并设置值
@Test
public void testCreateSetData() throws Exception{
String path = client.create().forPath("/app1","TaoBao".getBytes());
System.out.println("path===="+path);
System.out.println("data===="+client.getData().forPath("/app1"));
}
创建临时/持久节点
注:默认是持久化节点
/**
* 创建临时节点
* @throws Exception
*/
@Test
public void testCreateEphemeral() throws Exception{
//设定节点类型为临时节点(默认类型是持久化)
String path = client.create().withMode(CreateMode.EPHEMERAL).forPath("/app3");
System.out.println("path===="+path);
//保持5S,5S后断开连接,节点自动删除
Thread.sleep(5000);
}
创建多级节点
- 直接创建多级节点会报错
/**
* 创建多级节点
* @throws Exception
*/
@Test
public void testCreateMultilevel() throws Exception{
//直接创建会报错
String path = client.create().forPath("/app4/p1");
System.out.println("path===="+path);
}
- 正确方式:增加creatingParentsIfNeeded()
/**
* 创建多级节点
* @throws Exception
*/
@Test
public void testCreateMultilevel() throws Exception{
//creatingParentsIfNeeded() :如果父节点不存在,则创建父节点
String path = client.create().creatingParentsIfNeeded().forPath("/app4/p1");
System.out.println("path===="+path);
}
查询节点
/**
* 查询节点
* 1.查询节点数据 get
* 2.查询子节点 ls
* 3,查询节点状态信息 ls -s
*/
@Test
public void testGet() throws Exception{
//先创建多节点 1个父节点,3个子节点
client.create().forPath("/app1","FatherValue".getBytes());
client.create().creatingParentsIfNeeded().forPath("/app1/name1","Jack".getBytes());
client.create().creatingParentsIfNeeded().forPath("/app1/name2","Rose".getBytes());
client.create().creatingParentsIfNeeded().forPath("/app1/name3","Mike".getBytes());
//查询指定path的节点值
byte[] dataApp1 = client.getData().forPath("/app1");
System.err.println("/app1 value ==== "+new String(dataApp1));
byte[] dataApp1Name1 = client.getData().forPath("/app1/name1");
System.err.println("/app1/name1 value ==== "+new String(dataApp1Name1));
//查询指定Path节点的子节点名称
List<String> app1Children = client.getChildren().forPath("/app1");
System.err.println("/app1 Children === "+app1Children);
//查询节点状态信息
Stat stat = new Stat();
client.getData().storingStatIn(stat).forPath("/app1");
System.err.println("/app1 stat === "+stat);
}
修改节点
- 基本修改数据 setData.forPath(path,data);
/**
* 修改节点数据
*/
@Test
public void testSet() throws Exception{
//先新增1个节点,设置值为"aaaaaa"
client.create().forPath("/app1","aaaaaa".getBytes());
System.err.println("/app1 修改前 value ==== "+new String(client.getData().forPath("/app1")));
//修改后的值
client.setData().forPath("/app1","bbbbbb".getBytes());
System.err.println("/app1 修改后 value ==== "+new String(client.getData().forPath("/app1")));
}
- 根据版本修改数据 setData().withVersion(version).forPath(path,data)
version是需要自行查询,防止不同客户端操作之间的干扰
/**
* 根据版本号修改节点数据
*/
@Test
public void testSetWithVersion() throws Exception{
//先新增1个节点,设置值为"aaaaaa"
client.create().forPath("/app1","aaaaaa".getBytes());
Stat stat = new Stat();
client.getData().storingStatIn(stat).forPath("/app1");
int version = stat.getVersion();
System.err.println("/app1 修改前 version ==== "+version);
System.err.println("/app1 修改前 value ==== "+new String(client.getData().forPath("/app1")));
//修改值(带着Version)
client.setData().withVersion(version).forPath("/app1","bbbbbb".getBytes());
Stat stat2 = new Stat();
client.getData().storingStatIn(stat2).forPath("/app1");
int version2 = stat.getVersion();
System.err.println("/app1 修改后 version ==== "+version2);
System.err.println("/app1 修改后 value ==== "+new String(client.getData().forPath("/app1")));
}
删除节点
- 删除单个节点 delete().forPath("/app5")
- 删除带有子节点的节点 delete().deletingChildrenIfNeed().forPath("/app1")
- 必须成功的删除 delete().guaranteed().forPath("/app4")
- 带回调的删除
【带回调的删除】
/**
* 带回调的删除
*/
@Test
public void testDeleteWithCallback() throws Exception{
//先创建一个节点并设置值
client.create().forPath("/app3","aaaaaa".getBytes());
//然后
client.delete().guaranteed().inBackground(new BackgroundCallback() {
@Override
public void processResult(CuratorFramework client, CuratorEvent event) throws Exception {
System.err.println("/app3 callback data is delete...");
System.err.println("/app3 callback event === "+event);
}
}).forPath("/app3");
}
Watcher事件监听
- ZK允许用户在指定节点上注册一些Watcher,并且在一些特定事件触发的时候,ZK服务端会将事件通知到感兴趣的客户端上,该机制是Zookeeper实现分布式协调服务的重要特性。
- Zookeeper中引入了Watcher机制来实现了发布/订阅功能,能够让多个订阅者同时监听某一个对象,当一个对象自身状态变化时,会通知所有订阅者。
- Curator引入了Cache来实现对ZK的服务端事件的监听。
Zookeeper的三种Watcher
- NodeCache:只是监听某一个特定的节点
- PathChildrenCache:监听一个ZNode的子节点
- TreeCache:可以监听整个树上的所有节点(即当前节点和下一级的子节点),类似于PathChildrenCache和NodeCache的组合。
NodeCache监听特定节点
public class CuratorWatcherTest {
CuratorFramework client;
/**
* 创建连接
*/
@Before
public void testBefore(){
//重试策略配置
RetryPolicy retryPolicy = new ExponentialBackoffRetry(3000,10);
client = CuratorFrameworkFactory.builder()
.connectString("127.0.0.1:2181")
.sessionTimeoutMs(60*1000)
.connectionTimeoutMs(15*1000)
.namespace("myRootPath")
.retryPolicy(retryPolicy)
.build();
//开始连接
client.start();
}
/**
* 测试简单的NodeCache监听
* @throws Exception
*/
@Test
public void testNodeCache() throws Exception{
//1.创建NodeCache对象
NodeCache nodeCache = new NodeCache(client,"/app1");
//2.注册监听
nodeCache.getListenable().addListener(new NodeCacheListener() {
@Override
public void nodeChanged() throws Exception {
System.out.println("节点发生变化~");
//获取节点修改后的数据
System.out.println("当期节点数据为:"+new String(nodeCache.getCurrentData().getData()));
}
});
//3.开启监听
nodeCache.start();
//测试代码,防止关闭
while (true){
}
}
/**
* 关闭连接
*/
@After
public void close(){
if(null!=client){
client.close();
}
}
}
PathChildrenCache监听子节点
示例代码
/**
* PathChildrenCache:监听子节点变化
* @throws Exception
*/
@Test
public void testPathClientCache() throws Exception{
//1.创建监听对象
PathChildrenCache pathChildrenCache = new PathChildrenCache(client,"/app2",true);
//2.绑定监听器
pathChildrenCache.getListenable().addListener(new PathChildrenCacheListener() {
@Override
public void childEvent(CuratorFramework curatorFramework, PathChildrenCacheEvent event) throws Exception {
System.err.println("子节点发生变化,type==="+event.getType());
System.err.println(event);
switch (event.getType()){
case CHILD_ADDED:
System.err.println("新增子节点,path==="+event.getData().getPath()+",data==="+new String(event.getData().getData()));
break;
case CHILD_UPDATED:
System.err.println("修改了子节点,data="+event.getData().getPath()+",data==="+new String(event.getData().getData()));
break;
default:
System.err.println("其他操作~");
break;
}
}
});
//开启监听器
pathChildrenCache.start();
while (true){
}
}
- 新增一个子节点:create /myRootPath/app2/p1 111
- 修改子节点:set /myRootPath/app2/p1 aaa
TreeCache 监听当前节点和其所有下一级子节点
/**
* TreeCache:监听当前节点和其所有下一级子节点
* @throws Exception
*/
@Test
public void testTreeCache() throws Exception{
//1.创建监听器
TreeCache treeCache = new TreeCache(client,"/app3");
//2.注册监听
treeCache.getListenable().addListener(new TreeCacheListener() {
@Override
public void childEvent(CuratorFramework client, TreeCacheEvent event) throws Exception {
System.err.println("节点发生变化,event===="+event);
}
});
//3.开启监听器
treeCache.start();
while (true){
}
}
- 创建当前节点 create /myRootPath/app3
- 修改当前节点值 set /myRootPath/app3 0000
- 为当前节点新增子节点 create /myRootPath/app3/p1 111111
- 修改当前节点的子节点值 set /myRootPath/app3/p1 22222
- 删除当前节点的子节点 deleteall /myRootPath/app3/p1