Zookeeper-1 基本概念和基础操作

Zookeeper概述

什么是zookeeper

  • ZooKeeper
    • 一个主从架构的分布式框架、开源的对其他的分布式框架的提供协调服务(service)
  • Zookeeper 作为一个分布式的服务框架
    • 它提供类似于linux文件系统(有目录节点树)的简版文件系统来存储数据(所有Zookeeper中的文件都一样)
    • Zookeeper 维护和监控存储的数据的状态变化,通过监控这些数据状态的变化,从而达到基于数据的集群管理
    • 主要用来解决分布式集群中应用系统的一致性问题
      在这里插入图片描述

为什么要使用zookeeper

  • ZooKeeper简单易用,能够很好的解决分布式框架在运行中,出现的各种协调问题。

    • 比如集群master主备切换、节点的上下线感知、统一命名服务、状态同步服务、集群管理、分布式应用配置管理等等(hadoop+zookeeper实现HA)

Zookeeper基本概念

Zookeeper数据结构

Zookeeper主要由以下三个部分实现:

  • 简版文件系统(Znode)
    • 基于类似于文件系统的目录节点树方式的数据存储
  • 原语
    • 可简单理解成ZooKeeper的基本的命令
  • 通知机制(Watcher-监听器)

Zookeeper数据节点Znode

ZNode分为四类

持久节点临时节点
非有序节点createcreate -e
有序节点create -screate -s -e

持久节点

  • 创建节点/zk_test,并设置数据my_data

    create /zk_test my_data
    
  • 持久节点,只有显示的调用命令,才能删除永久节点

    delete /zk_test
    

临时节点

  • client1上创建临时节点

    create -e /tmp tmpdata
    
  • client2上查看client1创建的临时节点

    ls /
    
  • client1断开连接

    close
    
  • client2上观察现象,发现临时节点被自动删除

    ls /
    

有序节点

  • 创建有序节点的意义:
    • 防止多个不同的客户端在同一目录下,创建同名ZNode,由于重名,导致创建失败
  • 有序节点会在节点被创建时,Zookeeper会自动在其节点后追加一个整形数字
    • 这个整数是一个由父节点维护的自增数字
    • 提供了创建唯一名字的ZNode的方式
如何创建有序节点
  • 命令行使用-s选项
    在这里插入图片描述
    Curator编程,可添加一个特殊的属性:CreateMode.EPHEMERAL

Zookeeper基本操作

zkCli命令行

  • 启动ZooKeeper集群;在ZooKeeper集群中的每个节点执行此命令

    ${ZK_HOME}/bin/zkServer.sh start
    
  • 停止ZooKeeper集群(每个节点执行以下命令)

    ${ZK_HOME}/bin/zkServer.sh stop
    
  • 查看集群状态(每个节点执行此命令)

    ${ZK_HOME}/bin/zkServer.sh status
    
  • 使用ZooKeeper自带的脚本,连接ZooKeeper的服务器

    //nodexx为集群几点名 2181位通信端口,即配置文件${ZK_HOME}/conf/zoo/.cfg的ClientPort
    zkCli.sh -server node01:2181,node02:2181,node03:2181
    

客户端会随机连接server后指定的服务器中的一个,并不会顺序尝试

zkCli常用命令

  • 查看ZooKeeper根目录/下的文件列表

    ls /
    
  • 创建节点,并指定数据

    create /kkb	kkb
    
  • 修改节点的数据

    set /kkb kkb01
    
  • 删除节点

    delete /kkb
    

其他常用命令

在这里插入图片描述

API

创建节点API对比
-原生API

  String result = zk.create("/test", "testdata".getBytes(), 
  ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);
  • Curator编程
String zNodeData = "testData";
  client.create().
                creatingParentsIfNeeded().          //如果父目录不存在,则创建
                withMode(CreateMode.PERSISTENT).    //创建永久节点
                forPath("/testPath/childPath", zNodeData.getBytes());//指定路径及节点数据

原生API

不友好,不具体介绍

curator编程

Curator对ZooKeeper的api做了封装,提供简单易用的API
package cruator;

import org.apache.curator.framework.CuratorFramework;
import org.apache.curator.framework.CuratorFrameworkFactory;
import org.apache.curator.retry.RetryNTimes;
import org.apache.zookeeper.CreateMode;

import java.util.List;

public class ZKCrud {
    private static final String ZK_ADDRESS = "node01:2181,node02:2181,node03;2181";
    private static final String ZK_PATH = "testPath";

    static CuratorFramework client = null;

    //初始化,建立连接
    public static void init(){
        //10次 每次间隔3000毫秒
        RetryNTimes retryPolicy = new RetryNTimes(10, 3000);
        client = CuratorFrameworkFactory.newClient(ZK_ADDRESS, retryPolicy);
        client.start();

        System.out.println("zk client started successfully");
    }

    public static void clean(){
        System.out.println("close client");
        client.close();
    }

    //创建永久节点
    public static void createPersitsentZNode() throws Exception {
        String zNodeData = "testData";
        String s = client.create()
                .creatingParentContainersIfNeeded()                     //父节点不存在就创建父节点
                .withMode(CreateMode.PERSISTENT)                        //创建永久节点
                .forPath("/testPath/childPath/", zNodeData.getBytes());//指定路径及节点数据
        print(s);

    }

    //创建临时节点
    public static void createEphemeralZnode() throws Exception {
        String zNodeData = "testData";
        client.create()
                .creatingParentsIfNeeded()
                .withMode(CreateMode.EPHEMERAL)
                .forPath("/testEphemeraPath/childPath",zNodeData.getBytes());
    }

    //查询ZNode数据
    public static void getZnodeData() throws Exception {
        //查新列表
        print("ls","/");
        List<String> list = client.getChildren().forPath("/");
        for (String s :
                list) {
            System.out.println(s);
        }
        print(client.getChildren().forPath("/"));

        //查询数据
        print("get",ZK_PATH);
        if (client.checkExists().forPath(ZK_PATH) != null){
            print(client.getData().forPath(ZK_PATH));
        }else {
            print("节点不存在");
        }
    }

    //修改节点数据
    public static void setZNode() throws Exception {
        // 修改节点数据
        String data2 = "hello world";
        print("set", ZK_PATH, data2);

        if (client.checkExists().forPath(ZK_PATH) != null){
            print(client.setData().forPath(ZK_PATH));
            print("get", ZK_PATH);
            print(client.getData().forPath(ZK_PATH));
        }else {
            print("节点不存在");
        }
    }

    //删除节点
    public static void deleteZNode() throws Exception {
        // 删除节点
        print("delete", ZK_PATH);
        client.delete().forPath(ZK_PATH);

        print("ls", "/");
        print(client.getChildren().forPath("/"));
    }


    public static void main(String[] args) throws Exception {
        init();
        //createEphemeralZnode();
        getZnodeData();
        //deleteZNode();
        clean();

    }

    private static void print(String... cmds) {
        StringBuilder text = new StringBuilder("$ ");
        for (String cmd : cmds) {
            text.append(cmd).append(" ");
        }
        System.out.println(text.toString());
    }

    private static void print(Object result) {
        System.out.println(
                result instanceof byte[]
                        ? new String((byte[]) result)
                        : result);
    }
}

Curator监听器循环有效期

Watcher

引言:会话

会话的概念

  • 客户端要对ZooKeeper集群进行读写操作,得先与某一ZooKeeper服务器建立TCP长连接;此TCP长连接称为建立一个会话Session

  • 每个会话有超时时间:SessionTimeout

    • 当客户端与集群建立会话后,如果超过SessionTimeout时间,两者间没有通信,会话超时

会话的特点

  • 客户端打开同一个Session中的请求以FIFO(先进先出)的顺序执行;
    • 如客户端client01与集群建立会话后,先发出一个create请求,再发出一个get请求;
    • 那么在执行时,会先执行create,再执行get
  • 若打开两个Session,无法保证Session间,请求FIFO执行;只能保证一个session中请求的FIFO

会话的生命周期

在这里插入图片描述

  • 未建立连接
  • 正在连接
  • 已连接
  • 关闭连接

引言:客户端如何获取Zookeeper的最新数据

  • 方式一轮询:ZooKeeper以远程服务的方式,被客户端访问;客户端以轮询的方式获得znode数据,效率会比较低(代价比较大)
    在这里插入图片描述

  • 方式二基于通知的机制:

    • 客户端在znode上注册一个Watcher监视器
    • 当znode上数据出现变化,watcher监测到此变化,通知客户端
      在这里插入图片描述

什么是Watcher

  • 客户端在服务器端,注册事件监听器
    • watcher用于监听znode数据修改,节点增删等
    • 当监听到时间后,watcher会触发通知客户端

ZKCli设置Watcher

注意:Watcher是一个单次触发的操作
Curator编程中watcher可以设置循环有效

Watcher监听节点变化

监听的节点需要已经存在
#ls path [watch]
#node01 上执行 
ls /tmp watch

#node02 上执行
create /tmp /dir01 dir01-data

#观察node-01上变化
[zk: node-01:2181,node-02:2181,node-03:2181(CONNECTED) 87] 
WATCHER::

WatchedEvent state:SyncConnected type:NodeChildrenChanged path:/zk_test

Watcher监听znode数据变化

#监控节点数据的变化;
#node02上
get /zk_test watch

#node03上
set /zk_test "junk01"
#观察node2上cli的输出,检测到变化

节点上下线监控

  • 原理:
  1. 节点1(client1)创建临时节点
  2. 节点2(client2)在临时节点,注册监听器watcher
  3. 当client1与zk集群断开连接,临时节点会被删除
  4. watcher发送消息,通知client2,临时节点被删除的事件
  • 用到的zk特性:

    ​ Watcher+临时节点

  • 好处:

    ​ 通过这种方式,检测和被检测系统不需要直接关联(如client1与client2),而是通过ZK上的某个节点进行关联,大大减少了系统耦合

  • 实现:
    client1操作

	# 创建临时节点
	create -e /temp tmp-data

client2操作

	# 在/zk_tmp注册监听器
	ls /temp watch

client1操作

	# 模拟节点下线
	close

观察client2
在这里插入图片描述

Curator API设置watcher

public class CuratorWatcher {
    /**
     * Zookeeper info
     */
    private static final String ZK_ADDRESS = "note01:2181,node02:2181,node03:2181";
    private static final String ZK_PATH = "/zktest";

    public static void main(String[] args) throws Exception {
        // 1.Connect to zk
        CuratorFramework client = CuratorFrameworkFactory.newClient(
                ZK_ADDRESS,
                new RetryNTimes(10, 5000)
        );
        client.start();
        System.out.println("zk client start successfully!");

        //path cache
        ///zktest/b/a
        PathChildrenCache pathCache = new PathChildrenCache(client, ZK_PATH, true);

        //Listener for PathChildrenCache changes
        PathChildrenCacheListener listener = new PathChildrenCacheListener() {
            @Override
            public void childEvent(CuratorFramework client, PathChildrenCacheEvent event) throws Exception {
                switch (event.getType()) {
                    case CHILD_ADDED: {
                        System.out.println("Node added: " + ZKPaths.getNodeFromPath(event.getData().getPath()));
                        break;
                    }

                    case CHILD_UPDATED: {
                        System.out.println("Node changed: " + ZKPaths.getNodeFromPath(event.getData().getPath()));
                        break;
                    }

                    case CHILD_REMOVED: {
                        System.out.println("Node removed: " + ZKPaths.getNodeFromPath(event.getData().getPath()));
                        break;
                    }
                    default:
                        break;
                }
            }
        };
        //添加监听器
        pathCache.getListenable().addListener(listener);

        pathCache.start(PathChildrenCache.StartMode.BUILD_INITIAL_CACHE);
        System.out.println("Register zk pathCache successfully!");

        Thread.sleep(60000);
        pathCache.close();

        //关闭zk连接
        client.close();
    }

}

Zookeeper应用场景

在这里插入图片描述

  1. NameNode使用ZooKeeper实现高可用.
  2. Yarn ResourceManager使用ZooKeeper实现高可用.
  3. 利用ZooKeeper对HBase集群做高可用配置
  4. kafka使用ZooKeeper
    • 保存消息消费信息比如offset.
    • 用于检测崩溃
    • 主题topic发现
    • 保持主题的生产和消费状态
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值