ElasticJob-02-ZooKeeper梳理

183 阅读7分钟

Zookeeper概念

  • Zookeeper是一个分布式、开源的分布式应用程序的协调服务。
  • Zookeeper是一个树形目录服务,数据模型与Unix的文件系统目录树类似。
  • 主要功能包括:配置管理、分布式锁、集群管理。

ZooKeeper命令操作

ZooKeeper数据模型

image.png

  • 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();
        }
    }
}

image.png 注:如果没有给节点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"));
}

image.png

image.png

创建临时/持久节点

注:默认是持久化节点

/**
 * 创建临时节点
 * @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);
}

image.png

  • 正确方式:增加creatingParentsIfNeeded()
/**
 * 创建多级节点
 * @throws Exception
 */
@Test
public void testCreateMultilevel() throws Exception{
    //creatingParentsIfNeeded() :如果父节点不存在,则创建父节点
    String path = client.create().creatingParentsIfNeeded().forPath("/app4/p1");
    System.out.println("path===="+path);
}

image.png

查询节点

/**
 * 查询节点
 * 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);
}

image.png

修改节点

  • 基本修改数据 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")));
}

image.png

  • 根据版本修改数据 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")));
}

image.png

删除节点

  • 删除单个节点 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");
}

image.png

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();
        }
    }
}

image.png

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

image.png

  • 修改子节点:set /myRootPath/app2/p1 aaa

image.png

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

image.png

  • 修改当前节点值 set /myRootPath/app3 0000

image.png

  • 为当前节点新增子节点 create /myRootPath/app3/p1 111111

image.png

  • 修改当前节点的子节点值 set /myRootPath/app3/p1 22222

image.png

  • 删除当前节点的子节点 deleteall /myRootPath/app3/p1

image.png