Zookeeper基础学习

zookeeper

概述

是一个开源的分布式应用的服务,提供了更高级别的服务,包括:同步、配置维护、分组和命名。提供了优质的高性能、高可用性和严格有序的访问。

应用场景

数据发布/订阅

​ 数据发布/订阅,需要发布者将数据发布到zookeeper的节点上,供订阅者进行数据订阅,进而达到动态获取数据的目的,实现配置信息的集中式管理和数据的动态更新。

具有两种设置模式:

推模式

服务端主动将数据更新发送给所有订阅的客户端;

拉模式

客户端主动请求获取最新数据。

Zookeeper采用了推拉相结合的模式,客户端向服务端注册自己需要关注的节点,一旦节点数据发生改变,那么服务器就会向相应的客户端推送Watcher事件通知,客户端接收到次通知后没主动到服务端获取最新的数据。

命名服务

Zookeeper可以实现一套分布式全局唯一ID的分配机制。

通过调用Zookeeper节点创建的API接口就可以创建一个顺序节点,并且再API返回值中会返回这个节点的完整名字,利用此特性,可以生成全局id,其步骤如下:

  1. 客户端根据任务类型,在指定类型的任务下通过调用接口创建一个顺序接节点,如job-
  2. 创建完成后,会返回一个完整的节点名。如job-000000000001
  3. 客户端拼接type类型和返回值后,就可以作为全局唯一id,如type2-job-000000000001

分布式协调/通知

Zookeeper中特有的Watcher注册于异步通知机制,能够很好的实现分布式环境下不同机器,甚至不同系统之间的协调与通知,从而实现对数据变更的实时处理,

通常做法是不同的客户端都对Zookeeper上的同一节点进行watcher注册,监听数据节点的变化(包括节点本身和子节点),若数据节点发生变化,那么所有订阅的客户端都能够接受到相应的Watcher通知,并作出通知。

在绝大多数分布式系统中,系统机器件的通信无外乎心跳检测工作进度汇报系统调度

分布式锁

​ 分布式锁用于控制分布式系统之间同步访问共享资源的一直方式,可以保证不同系统访问一个或一组资源时的一致性,主要分为排他锁和共享锁。

排他锁又被称为写锁或独占锁,若事务A对数据对象01加上了排他锁,那么整个加所期间,只允许事务A对01进行读取和更新操作。其他任何事务不允许操作。

共享锁被称为读锁,若事务A对数据对象01加上共享锁,那么当前事务只能对01进行读取操作,其他四五也只能对这个数据对象加共享锁,知道该数据对象上的所有共享锁都被释放。

分布式队列

集群环境搭建

服务器ip 主机名 myid的值
** node01 1
** node02 2
** node03 3

步骤:

第一步:下载zookeeper的压缩包,

第二步:修改配置文件 conf/zoo_conf

image-20201020103256879

第三步:添加myid配置

在/export/servers/zookeeper-***/zkdatas 创建一个文件,文件名为myid,文件内容为1.

第四步:启动项目

image-20201020103600033

image-20201020103625362

结构类型

数据模型

image-20201020104308940

zk的数据存储是是结构化存储,没有文件和目录的概念,文件和目录被抽象成了节点(node),上图中的每个node称为znode, 每个znode由三个部分组成:

  1.  stat, 描述该znode的版本权限等信息。
  2.  data,与该znode关联的数据。
  3.  children, 该znode下的子节点
       zk除了提供api接口外,同样提供了shell接口来进行相应的操作。

节点类型

ZooKeeper节点znode有两种类型,临时节点(ephemeral)和持久节(persistent)。znode的类型在创建时确定并且之后不能再修改。

ephemeral节点在客户端会话结束时,将会被zookeeper删除,并且ephemeral节点不可以有子节点。

persistent节点不依赖与客户端会话,只有当客户端明确要删除该persistent节点时才会被删除。

znode还有一个序列化的特性,如果创建的时候指定的话,该Znode的名字后面会自动追加一个不断增加的序列号,序列号对此此节点的父节点来说是唯一的,它便会记录每个子节点创建的先后顺序,它的格式是'%10d'.

此时就会出现四种类型的Znode节点,分别对应:

  • Persisent:永久节点
  • Ephemeral:临时节点
  • Persisent_sequential:永久节点、序列化
  • Ephemeral_sequential:临时节点、序列化

shell命令

命令 说明 参数
create [-s] [-e] path data acl 创建Znode -s指定顺序节点 -e 指定是临时节点,如果无参数,表示永久化节点
ls path [watch] 列出Path下所有子Znode
ls2 path [watch] 查看path下所有znode以及子Znode的属性
set path data [version] 更新节点 version数据版本
get path [watch] 获取path对应的Znode的数据和属性
delete path [version] 删除节点 version数据版本
history 历史记录

属性:

image-20201020110708865

dataversion:数据版本号,进行更新操作加一。

cversion: 子节点的版本号

aclVersion:ACL的版本号

cZxid:Znode创建的事务id。

mZxid: Znode被修改的事务id

ctime:创建时间

mtime:更新时间

ephemeralOwner:临时节点的会话id

watch机制

image-20201020111116102

javaAPi操作

推荐使用JavaApi的是一套封装好的zookeeper客户端框架Curator,解决了很多zk客户端底层的细节。

Curator包含的几个包

  • curator-framework:对zookeeper的底层api的一些封装

  • curator-recipes:封装了一些高级特性,如:cache事件监听,选举,分布式锁,分布式计数器。

    /**
         * 创建节点
         */
        @Test
        void createNode() throws Exception {
            // 定义要连接的zk的所在虚拟机路径
    
            ExponentialBackoffRetry retry = new ExponentialBackoffRetry(3000, 3);
            // 获取客户端对象
            CuratorFramework client = CuratorFrameworkFactory.newClient(connString, retry);
            // 开启客户端操作
            client.start();
            // 创建永久节点
            client.create().creatingParentsIfNeeded().withMode(CreateMode.PERSISTENT).forPath("/hello1");
            // 修改节点数据
            client.setData().forPath("/hello1","hello".getBytes());
            // 查询节点数据
            byte[] bytes=client.getData().forPath("/hello1");
            System.out.println(new String(bytes));
            client.close();
        }
    
    @Test
        public void myWatch() throws Exception {
            CuratorFramework client = CuratorFrameworkFactory
                    .newClient(connString,
                            new ExponentialBackoffRetry(3000, 0));
            client.start();
            TreeCache treeCache = new TreeCache(client, "/createNode03");
            // 获取监听容器
            treeCache.getListenable().addListener(new TreeCacheListener() {
                @Override
                public void childEvent(CuratorFramework curatorFramework, TreeCacheEvent treeCacheEvent) throws Exception {
                    GetDataBuilder data = client.getData();
                    if(data != null){
                        switch(treeCacheEvent.getType()){
                            case NODE_ADDED:
                                System.out.println("监听添加节点------------");
                                break;
                            case NODE_UPDATED:
                                System.out.println("更新节点数据");
                            default:break;
                        }
                    }
    
                }
            });
            treeCache.start();
            Thread.sleep(50000);
    
        }
    
posted @ 2020-11-14 16:22  且I听  阅读(99)  评论(0)    收藏  举报