zookeeper入门教程

117 阅读6分钟

zookeeper概念

    • zookeeper用例管理hadoop, hive ,pig的管理员
  • 提供的功能:
    • 配置管理
      • 服务的配置信息放在zookeeper中,修改配置文件后不用重启服务,直接生效,可以管理数百个服务的配置文件。
    • image.png
    • 分布式锁

image.png

  -  集群管理
  

image.png

zookeeper的安装

使用docker進行安裝:

cloud.tencent.com/developer/a… 注意:

本用例安装的是zookeeper3.8版本
docker run -p 2181:2181 --name zookeeper-standalone --restart always -d zookeeper:3.8
docker exec -it zookeeper-standalone /bin/bash

zookeeper命令操作

数据模型

image.png

  • ZNode節點,可以存放少量数据(1MB),也允许有子节点。
  • 节点类型:
    • PERSISTENT: 持久节点
    • EPHEMERAL:临时节点 -e
    • PERSISTENT_SEQUENTIAL 持久化顺序节点 -s
    • EPHEMERAL_SEQUENTIAL 临时顺序节点 -es

命令操作:

服务端命令

image.png

客户端命令

image.png

  • 默认创建节点是持久化的。
  • 创建临时节点 -e
    • create -e
    • 临时节点是会话级别的,当服务端关闭后节点就丢失了。

image.png

zookeeper的javaApi操作

Curator介绍 & Curator API常见操作

Curator要和zookeeper版本对应起来。

建立链接

使用Curator建立链接
支持命名空间的设置。

添加节点

- 创建路径
- 创建节点+数据: create().forPath(“”,data)
- 默认类型:持久化的。使用withMode()指令节点的类型。
    - 临时节点javaClient与zookeeper服务断开会删除。
-如果父节点不存在则创建父节点。

删除

  • 删除单个节点: delete().forPath()
  • 删除带有子节点的节点
  • 必须成功的删除:本质就是重试 -回调:绑定一个回调函数,节点删除之后自动直接回调方法。

修改

  • 修改数据 setData().forPath()
  • 根据版本修改: setData().withVersion().forPath()

image.png

查询

  • 查询数据get() :getData().forPath()
  • 查询子节点:ls: getChildren().forPath()
  • 查询节点状态信息:ls -s: getData().storingStatIn(对象).forPath()

watch监听方法:

gitee.com/everan/zook…

概念

当事件发生变更的时候通知到注册的观察者上

image.png

javaApi 实现NodeCache模式

  • 监听当前节点的增删改查。
  • 监听当前节点变化的值

javaApi 实现PathChildrenCache模式

  • 监听子节点的增删改查
  • 监听子节点数据修改之后的变化的数据

javaApi 实现TreeCache模式

watch 事件的代码实现

以上代码


public class ZookeeperWatchTest {

    private CuratorFramework client;

    public static Logger log=LogManager.getLogger(ZookeeperWatchTest.class);

    /**
     * 建立链接
     */
    @Before
    public void testConnect(){

        //方法1:使用构造函数
        RetryPolicy retryPolicy = new ExponentialBackoffRetry(1000, 10);
      // client = CuratorFrameworkFactory.newClient("127.0.0.1:2181", 1000, 1000, retryPolicy);

        //方法2:使用build链式编程
       client= CuratorFrameworkFactory.builder()
        .connectString("127.0.0.1:2181")
                .sessionTimeoutMs(60*1000)
                        .connectionTimeoutMs(15*1000)
                                .retryPolicy(retryPolicy)
                                        .namespace("itheima")
                                                .build();
        client.start();
    }


  //使用NodeCache模式
    @Test
    public void  testNodeCahce() throws InterruptedException {
        CuratorCache curatorCache = CuratorCache.build(client, "/app1");
        //创建监听
        curatorCache.listenable().addListener(new CuratorCacheListener() {

            // 第一个参数:事件类型(枚举)
            // 第二个参数:节点更新前的状态、数据
            // 第三个参数:节点更新后的状态、数据
            // 创建节点时:节点刚被创建,不存在 更新前节点 ,所以第二个参数为 null
            // 删除节点时:节点被删除,不存在 更新后节点 ,所以第三个参数为 null
            // 节点创建时没有赋予值 create /curator/app1 只创建节点,在这种情况下,更新前节点的 data 为 null,获取不到更新前节点的数据
            @Override
            public void event(Type type, ChildData childData, ChildData childData1) {
                switch (type.name()){

                    case "NODE_CREATED":
                        if(childData!=null){
                            System.out.println("创建了节点:"+ new String( childData1.getData()));
                        }
                        break;
                    case "NODE_CHANGED":
                        if(childData!=null){
                            System.out.println("更新了节点:"+ new String( childData1.getData()));
                        }else {
                            System.out.println("节点第1次赋值");
                        }
                        break;

                    case "NODE_DELETED":
                        System.out.println(childData.getPath()+"节点已经删除");
                        break;
                    default:
                        break;

                }
            }
        });
        //开启监听
        curatorCache.start();
        //延迟结束
        Thread.sleep(1000*60*10);

    }


    //使用PathChildrenCache模式,方法已经过时
    @Test
    public void  testPathChildrenCache() throws Exception {

        PathChildrenCache pathChildrenCache = new PathChildrenCache(client, "/app2", true);

        //创建监听
        pathChildrenCache.getListenable().addListener(new PathChildrenCacheListener() {
            @Override
            public void childEvent(CuratorFramework curatorFramework, PathChildrenCacheEvent event) throws Exception {
                System.out.println("子节点发生了变化");
                PathChildrenCacheEvent.Type type = event.getType();
                if(type.equals(PathChildrenCacheEvent.Type.CHILD_ADDED)){
                System.out.println("新增子节点"+new String(event.getData().getPath() +":"+new String( event.getData().getData())));
                }
                if(type.equals(PathChildrenCacheEvent.Type.CHILD_UPDATED)){
                    System.out.println("修改子节点"+new String(event.getData().getPath() +":"+new String(  event.getData().getData())));
                }
                if(type.equals(PathChildrenCacheEvent.Type.CHILD_REMOVED)){
                    System.out.println("删除子节点"+new String( event.getData().getPath() +":"+new String(  event.getData().getData())));
                }
            }
        });
        //开启监听set
        pathChildrenCache.start();
        //延迟结束
        Thread.sleep(1000*60*10);

    }

    /**
     * 使TreeCache: 监听自身节点以及后续节点的变化
     * @throws InterruptedException
     */
    @Test
    public void  testTreeCache() throws Exception {
        TreeCache treeCache = new TreeCache(client, "/app2");

        //创建监听
        treeCache.getListenable().addListener(new TreeCacheListener() {
            @Override
            public void childEvent(CuratorFramework curatorFramework, TreeCacheEvent event) throws Exception {

                System.out.println("节点发生了变化");
                TreeCacheEvent.Type type = event.getType();
                if(type.equals(TreeCacheEvent.Type.NODE_ADDED)){
                    System.out.println("新增节点"+new String(event.getData().getPath() +":"+new String( event.getData().getData())));
                }
                if(type.equals(TreeCacheEvent.Type.NODE_UPDATED)){
                    System.out.println("修改节点"+new String(event.getData().getPath() +":"+new String(  event.getData().getData())));
                }
                if(type.equals(TreeCacheEvent.Type.NODE_REMOVED)){
                    System.out.println("删除节点"+new String( event.getData().getPath() +":"+new String(  event.getData().getData())));
                }
            }
        });

        //开启监听set
        treeCache.start();
        //延迟结束
        Thread.sleep(1000*60*10);

    }

    @After
    public void destory(){
        if(client!=null){
            client.close();
        }
    }

}

分布式锁的实现

概述

  • 单机应用并发,设计并发同步,采用synchronized或者lock的方式来解决多线程间的代码同步问题。这是多线程运行在同一个JVM之下,没有问题。
  • 跨JVM工作环境,需要更高级的锁机制【跨级机器的进程之间的数据同步问题】——分布式锁
    • 基于缓存实现分布式锁:redis: master一点挂了,则可能多个服务都获得分布式锁,不可靠。优点是:性能高。
    • 基于zookeeper实现:性能比数据库高,且可靠。
    • 基于数据库实现:性能较低。

image.png

分布式锁原理

  • Curator已经完全实现。
  • 客户端连接获取锁是,在lock节点下创建【临时顺序】节点:这样如果client1客户端挂了,则临时 节点会自动删除。

image.png

使用Curator实现分布式锁API

案例:模拟12306卖票

image.png

具体代码

public class Ticket12306 implements Runnable{

    private InterProcessMutex lock;



    public Ticket12306(){

        RetryPolicy retryPolicy = new ExponentialBackoffRetry(1000, 10);
        // client = CuratorFrameworkFactory.newClient("127.0.0.1:2181", 1000, 1000, retryPolicy);

        //方法2:使用build链式编程
        CuratorFramework   client= CuratorFrameworkFactory.builder()
                .connectString("127.0.0.1:2181")
                .sessionTimeoutMs(60*1000)
                .connectionTimeoutMs(15*1000)
                .retryPolicy(retryPolicy)
                .build();
        client.start();
        lock=new InterProcessMutex(client, "/lock");


    }

    private int tickQty=10;


    @Override
    public void run() {
        while (true){
            try {
                lock.acquire(2, TimeUnit.SECONDS);
                if(tickQty>0){
                    tickQty--;
                    System.out.println("剩余票数:" +tickQty+ Thread.currentThread().getName()+"购票成功");
                    Thread.sleep(100);
                }

            } catch (Exception e) {
                throw new RuntimeException(e);
            }finally {
                try {
                    lock.release();
                } catch (Exception e) {
                    throw new RuntimeException(e);
                }
            }
        }
    }
}

Zookeeper 集群搭建

zookeeper介绍

image.png

  • 如果启动了3台,则3号最大,且获得票数超过半数,所以是leader,但是如果后来再有4号机器进入,因为已经有leader(3号),所以不会重新进行选举,4号此时不会成为新的leader。

集群搭建:

集群故障测试

集群角色

  • Leader:处理事务请求(增删改)
  • Follower: 1,处理非事务请求,转发事务请求给Leader服务器2,产出Leader选举投票。
  • Observer:处理非事务请求,转发事务请求给Leader服务器.【非实物请求较多,为了分单foller的压力,所以存在Observer角色。】

image.png

链接zookeeper集群

public void testConnect(){

    //方法1:使用构造函数
    RetryPolicy retryPolicy = new ExponentialBackoffRetry(1000, 10);
  // client = CuratorFrameworkFactory.newClient("127.0.0.1:2181", 1000, 1000, retryPolicy);

    //方法2:使用build链式编程
   client= CuratorFrameworkFactory.builder()
    .connectString("127.0.0.1:2181,127.0.0.1:2182,127.0.0.1:2183")
            .sessionTimeoutMs(60*1000)
                    .connectionTimeoutMs(15*1000)
                            .retryPolicy(retryPolicy)
                                    .namespace("itheima")
                                            .build();
    client.start();
}

附录

配套视频:

www.bilibili.com/video/BV1M7…