Bootstrap

zookeeper入门(一)

1.概述:

zookeeper提供了中心化的服务:统一配置信息,命名,提供分布式锁,提供组服务。

如下图所示,hive和flume都是基于Hadoop的,这些框架都到zookeeper中进行注册,当有其中一个框架的信息发生改变,就可以使用zookeeper来进行通知,告诉其他框架。
在这里插入图片描述

2.zookeeper安装的三种模式:

  • 单机模式:只在一台服务器上安装,只能启动这个框架的部分功能
  • 伪分布式:只在一台服务器上安装,能提供这个框架的大部分甚至全部功能
  • 完全分布式:在集群中安装,能提供这个框架的所有功能

Linux单机安装方法:
1.进入Linux中,把目录切换到/home/software

cd /home/software

2.下载Zookeeper的安装包,直接再software目录下复制下载地址:

wget http://bj-yzjd.ufile.cn-north-02.ucloud.cn/zookeeper-3.4.8.tar.gz

3.解压安装包

tar -xvf zookeeper-3.4.8.tar.gz

4.进入解压好的zookeeper文件夹中的conf文件

cd zookeeper-3.4.8/conf

5.复制conf中的zoo_sample.cfg,启动zookeeper的时候,会来加载这个配置文件,而zoo_sample.cfg是zookeeper给我们准备好的案例配置模板,我们需要做的就是复制出来一份

cp zoo_sample.cfg zoo.cfg

6.编辑zoo.cfg并且把dataDir修改成我们指定的路径,dataDir中的路径放的是快照,默认/tmp,如果不自己指定,tmp中的文件会自动清除。

vim zoo.cfg
dataDir=/home/software/zookeeper-3.4.8/tmp

7.进入Zookeeper安装目录的子目录bin下

cd ../bin

8.启动Zookeeper ,在bin下产生启动日志zookeeper.out,如果启动失败,那么请查看zookeeper.out 通过jps查看,如果出现QuorumPeerMain表示Zookeeper启动了 或者通过sh zkServer.sh status来查看,在单机模式下如果出现standalone表示成功

sh zkServer.sh start

9.启动客户端

sh zkCli.sh

3.zookeeper的结构:

zookeeper中信息的储存是以节点的形式。
比如下图中,zookeeper中挂载了一个Hadoop服务器,一个Log日志服务器,一个video视频服务器。而这些挂载的服务器下面又可以挂载其他的服务器。
在这里插入图片描述
关于zookeeper节点你需要知道的一些特点:

  • Zookeeper本身是一个树状结构,根节点是/
  • 每一个节点都称之为是一个Znode节点 - Znode树
  • 每一个节点必须携带数据,这个数据一般是对节点的描述或者是配置信息
  • 在Zookeeper中,不存在相对路径,所有的节点必须从根路径开始计算
  • Zookeeper会将Znode树维系在内存以及磁盘中
    a.维系在内存中的目的是为了查询快
    b.维系在磁盘中的目的是为了可靠
  • 数据在磁盘上的存储位置dataDir来决定
  • 在Zookeeper中,会将每一个写操作(connect,create,set,delete,rmr等)看作是一个事务。Zookeepe会给事务分配一个全局递增的编号,这个编号称之为事务id,简写为Zxid,事务id在计算的时候,只需要关心是整个过程中的第几个写操作即可
  • 临时节点不能挂载子节点,持久节点可以挂载子节点
  • 在Zookeeper中不允许存在同名节点

zookeeper中的命令:

在这里插入图片描述

节点种类:

零时节点: zookeeper关闭后节点消失,创建的时候加-e
持久节点: zookeeper关闭后节点不消失,创建的时候什么都不加
顺序节点: 节点会根据当前已存在的节点数自动加 1
非顺序节点: 没有顺序值

根据上面四种可以组合为:零时顺序,零时非顺序,持久顺序,持久分顺序

zookeeper中的节点信息:

在这里插入图片描述
解释事务id变化规律:
每一个写操作(创建,修改等),都会被分配一个全局递增的事务IP,在创建了一个znode节点的时候,cz,mz,pz的值都是一样的。
在这里插入图片描述
首先是根节点/,在创建的时候,都是0。

其次是创建一个节点/a,根目录的事务id变化很容易理解就不解释,我们看/a的事务id变化,首先是节点创建的事务id为1,又因为在创建了一个znode节点的时候,cz,mz,pz的值都是一样的。所以另外两个也是1.

我们再来看创建节点/b,/中的pzxid变为了2,因为这时/下的第二个创建节点,子节点发生了变化,所以是2。/a没有和上面一样。/b是第二个创建的节点,所以czxid为2,又因为在创建了一个znode节点的时候,cz,mz,pz的值都是一样的。所以另外两个也是2.

最后来看再a节点下创建子节点a1,先看/的pzxid,因为/下的子节点个数没有变化所以还是2。/a的pzxid,a下创建了a1,是全局的第三个事务,所以是3,a下子节点第一次变,所以是1
在这里插入图片描述

java中使用zookeeper:

1.注入依赖:

<dependencies>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.10</version>
        </dependency>
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-simple</artifactId>
            <version>1.6.4</version>
            <scope>compile</scope>
        </dependency>
        <dependency>
            <groupId>org.apache.zookeeper</groupId>
            <artifactId>zookeeper</artifactId>
            <version>3.4.8</version>
        </dependency>
    </dependencies>

2.连接zookeeper:

 private ZooKeeper zk;

    // 连接Zookeeper
    @Before
    public void connect() throws IOException, InterruptedException {
        // @Test和main起的作用是一致的
        // main所在的类是一个线程类 - 主线程
        // Zookeeper底层连接过程使用的是Netty连接
        // Netty:异步非阻塞连接
        // 非阻塞:无论连上连不上都会继续向下执行
        // 异步:当前连接和监控线程与主线程之间是抢占的
        // 连接可能还没连完或者还没有来得及监控,主线程可以提前抢占执行
        // 需要等连接连接且监控到了,主线程才能继续执行
        CountDownLatch cdl = new CountDownLatch(1);
        zk = new ZooKeeper(
                "10.42.130.141:2181", // 连接地址+端口号,请写自己的IP
                5000, // 会话超时时间,现阶段这个值必须在4000~4000之间,单位是毫秒
                new Watcher() {
                    // 监控连接事件
                    @Override
                    public void process(WatchedEvent event) {
                        if (event.getState() == Event.KeeperState.SyncConnected)
                            System.out.println("连接已经建立~~~");
                        cdl.countDown();
                    }
                }
        );
        cdl.await();
        System.out.println("finish");
    }

3.创建节点:

 @Test
    public void createNode() throws KeeperException, InterruptedException {
        // 返回值表示节点名
        String path = zk.create("/log", "log servers".getBytes(),
                ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);
        System.out.println(path);
    }

4.其他操作:

// 修改数据
    @Test
    public void setData() throws KeeperException, InterruptedException {
        // 第三个参数version对应的dataVersion
        // 在修改数据的时候,先比较version是否一致,如果不一致则直接报错
        // 如果version为-1,那么表示忽略版本校验强制修改
        // 返回值表示节点信息
        Stat stat = zk.setData("/log", "log server".getBytes(), -1);
        System.out.println(stat);
    }

    // 获取数据
    @Test
    public void getData() throws KeeperException, InterruptedException {
        // 如果需要节点信息,那么可以传入一个Stat对象
        // Stat s = new Stat();
        // byte[] data = zk.getData("/log", null, s);
        // System.out.println(s);
        // 如果不需要节点信息,那么可以传入null
        byte[] data = zk.getData("/log", null, null);
        System.out.println(new String(data));
    }

    // 删除节点
    @Test
    public void deleteNode() throws KeeperException, InterruptedException {
        // 在删除的时候也会进行版本校验
        //要求节点下没有子节点
        zk.delete("/log", -1);
    }

    // 获取子节点
    @Test
    public void getChildren() throws KeeperException, InterruptedException {
        // 将子节点的名称放到一个List中返回
        List<String> cs = zk.getChildren("/", null);
        for (String c : cs) {
            System.out.println(c);
        }
    }

    // 判断节点是否存在
    @Test
    public void exist() throws KeeperException, InterruptedException {
        // 如果节点存在,则返回一个Stat对象
        // 如果节点不存在,则返回null
        Stat s = zk.exists("/log", null);
        System.out.println(s == null);
    }
    // 监控节点数据是否发生改变
    @Test
    public void dataChanged() throws KeeperException, InterruptedException {
        CountDownLatch cdl = new CountDownLatch(1);
        zk.getData("/log", new Watcher() {
            @Override
            public void process(WatchedEvent event) {
                if (event.getType() == Event.EventType.NodeDataChanged)
                    // 实际开发中,这儿应该产生日志:IP,时间,数据等
                    System.out.println("节点数据被改变");
                cdl.countDown();
            }
        }, null);
        cdl.await();
    }
    // 监控子节点个数是否发生变化
    @Test
    public void childChanged() throws KeeperException, InterruptedException {
        CountDownLatch cdl = new CountDownLatch(1);
        zk.getChildren("/log", new Watcher() {
            @Override
            public void process(WatchedEvent event) {
                if (event.getType() == Event.EventType.NodeChildrenChanged)
                    System.out.println("子节点个数发生变化");
                cdl.countDown();
            }
        });
        cdl.await();
    }

    // 监控节点的创建或者删除
    @Test
    public void nodeChanged() throws KeeperException, InterruptedException {
        CountDownLatch cdl = new CountDownLatch(1);
        zk.exists("/music", new Watcher() {
            @Override
            public void process(WatchedEvent event) {
                if (event.getType() == Event.EventType.NodeCreated)
                    System.out.println("节点被创建");
                else if (event.getType() == Event.EventType.NodeDeleted)
                    System.out.println("节点被删除");
                cdl.countDown();
            }
        });
        cdl.await();
    }
;