zookeeper

什么是zk

定义与作用

定义:

​ 是树形目录服务,Haddoop下的一个子项目

​ 分布式,开源的分布式应用程序的协调服务(中间件)

​ 【redis也是一个缓存中间件】

作用:

​ 管理大数据下的框架

支持功能

1.配置管理(注册中心)

获取提供方和使用方的信息,整体提供所有服务器的配置功能 如:ip,端口,服务提供的功能接口等

2.分布式锁

当集群使用分布式开发的时候,用于控制服务的资源,锁住后只可一个服务对某一功能可操作

3.集群管理

管理服务的节点(多台服务器提供统一的功能,组成集群。保证服务可以一直运转。)

命令操作

数据模型

每个节点不仅可以有子节点还可以存储1M的数据 每个节点的名字都是/开头

Zookeeper为了保证高吞吐和低延迟,在内存中维护了这个树状的目录结构,这种特性使得Zookeeper不能用于存放大量的数据,每个节点的存放数据上限为1M

默认有一个根节点,每个子节点被称为ZNode

节点分类

【临时节点只要重启就消失】

​ 1.持久化节点 客户端断开,节点仍然存在

​ 2.临时节点 客户端断开,节点自动删除

​ 3.持久化顺序节点 -s 【每创建一个,系统自动增长+1】

​ 4.临时顺序节点 -es 创建时 顺序+1 ,客户端断开,节点自动删除

在这里插入图片描述

服务端

在linux中生效

API作用
./zkServer.sh start开启
./zkServer.sh status查看状态
./zkServer.sh stop创建节点
./zkServer.sh重启

客户端

使用时,必须先开启服务端

连接 zkCli -server IP:PORT

默认端口号2181。配置文件中可改

【只要是写具体某一节点,都是从根开始写】

API作用
ls /节点查看当前节点下的子字节点
ls -s /节点查看该节点的详细信息
create /节点创建节点
get /节点获取节点的值
set /节点 值设置节点值
delete /节点删除节点
deleteall /节点递归删除节点

Curator的API

Curator三方 apache的Java客户端库

操作步骤

1.导入依赖

2.导入编译插件

两种方式建立连接获得curatorFramwork

第二种方式可以指定命名空间,默认创建在该命名空间下

注意:如果该空间没有子节点,过一定时间该命名空间的节点会消失

   @Before
    public void testConnection(){
        //第一种方式获取连接

        //创建重连策略对象
        RetryPolicy retryPolicy = new ExponentialBackoffRetry(1000, 2);  重连间隔,次数
        //创建连接对象
        /**
         * 需要:
         *       1.连接地址和端口号 ,多个以逗号隔开
         *       2.会话超时时间
         *       3.连接超时时间
         *       4.重试策略
         *       5.设置命名空间(可选设置)
          */
         cf = CuratorFrameworkFactory.newClient("localhost:2181",
                30000, 15000, retryPolicy);
        //开启连接
        cf.start();

            //第二种方式获取连接【链式编程】
//        CuratorFramework build = CuratorFrameworkFactory.builder()
//                .connectString("localhost:2181")
//                .sessionTimeoutMs(30000)
//                .connectionTimeoutMs(15000)
//                .retryPolicy(retryPolicy)
//                .build();
//        build.start();
    }
添加节点

默认类型为持久化

如果没有指定值, 会有一个默认值,默认值为自己电脑IP

client 是一次会话,每次创建的连接也是一次会话

提供方法,支持跨级创建节点【本质上如果没有父节点是不允许创建子节点的】

1.基本创建

2.带参数创建

3.设置类型

4.创建多级节点

当系统异常连接中断,zk有一个缓冲机制,不会立即将临时节点清除,如果重连不成功,隔一段时间再自动清除

    /**
     创建一个节点
     如果在创建时没有设置值,那么就以当前主机ip作为数据进行存储
     */
    @Test
    public void show() throws Exception {
        //创建一个节点
        String s = cf.create().forPath("/c");
        System.out.println(s);   //返回路径
    }

    /**
     创建一个节点,同时存储数据
     存储时 使用的是字节进行存储 所以需要用getBytes()转码
     */
    @Test
    public void show1() throws Exception {
        //创建一个节点
        String s = cf.create().forPath("/d","23".getBytes());
        System.out.println(s);   //返回路径
    }

    /**
     创建时修改存储类型。
     默认类型:持久化
     */
    @Test
    public void show2() throws Exception {
        //创建一个节点
        String s = cf.create().withMode(CreateMode.PERSISTENT_SEQUENTIAL_WITH_TTL).forPath("/c/cpp1","23".getBytes());
        System.out.println(s);   //返回路径

//        String path = cf.create().withMode(CreateMode.EPHEMERAL).forPath("/a/app2");
//        System.out.println(path);
    }


    /**
     curator提供方法 可以多级创建
     */
    @Test
    public void show3() throws Exception {
        //创建一个节点
        String s = cf.create().creatingParentContainersIfNeeded().forPath("/a/app1/app2/app3","12".getBytes());
        System.out.println(s);

        
        //使用回调,相当于是监听,当创建时就会显示回调函数内容
        cf.create().creatingParentContainersIfNeeded().inBackground(new BackgroundCallback() {
            @Override
            public void processResult(CuratorFramework curatorFramework, CuratorEvent curatorEvent) throws Exception {
                System.out.println("我被填满了~");
                System.out.println(curatorEvent);
            }
        }).forPath("/a/app1/app2/app3");
        }
查询节点

查询数据 get

getData().forpath()

查询子节点 ls

getchildren().forpath()

使用方法 getchildren如果设置了命名空间,使用/ 是在命名空间下开始查找,需要加上命名空间

查询状态信息 ls -s

getData().storingtatuin().forpath()

/**
   获取某一节点的数据
   如果某节点为null则报错
 */
@Test
public void show4() throws Exception {
    byte[] bytes = cf.getData().forPath("/b/bpp1");
    System.out.println(new String(bytes));
}


/**
    获取某一节点的子节点

 */
@Test
public void show5() throws Exception {
    List<String> strings = cf.getChildren().forPath("/a/app1");
    System.out.println(strings);
}

/**
    获取某一节点的状态信息
 */
@Test
public void show6() throws Exception {
    Stat status = new Stat();  //创建状态对象
    System.out.println(status);  //首次输出  全是默认值

    //3. 查询节点状态信息命令:ls -s /路径
    cf.getData().storingStatIn(status).forPath("/a/app1");
    System.out.println(status);  //再次输出  已经进行了封装

}
修改节点

基本修改setData().forpath(key,value.getBytes)

根据版本修改 目的:其他线程不可干扰。保证原子性

new Statu . getVersion;

setData().withVersion(版本号)forpath(key,value.getBytes)

不支持重命名节点 【删除后重新添加】

/**
 *  基础设置数据
 */
@Test
public void show7() throws Exception {
   cf.setData().forPath("/a/app1", "itcast".getBytes());
}


/**
 *  通过版本限定(类似于锁机制,当前时刻其他人不可同时修改),修改数据
 */
@Test
public void show8() throws Exception {
    //新建状态对象
    Stat stat = new Stat();

    //获取当前节点的状态信息
    byte[] bytes = cf.getData().storingStatIn(stat).forPath("/a/app1");

    //获取版本号
    int version = stat.getVersion();

    //通过该版本号 锁定当前节点进行修改
    Stat stat1 = cf.setData().withVersion(version).forPath("/a/app1", "hha".getBytes());

}
删除节点

1.删除单个节点

2.删除含字节点的节点

3.必定成功删除

4.回调【增删改查均有】

/**
 * 基础删除功能
 * @throws Exception
 */
@Test
public void show9() throws Exception {
    // 1. 删除单个节点
    cf.delete().forPath("/d");
}


/**
 * 多级目录删除
 * @throws Exception
 */
@Test
public void show10() throws Exception {
     cf.delete().deletingChildrenIfNeeded().forPath("/a/app1");
}

/**
 * 防止网络波动,必定删除成功(底层是如果失败,则多次重连。)
 * @throws Exception
 */
@Test
public void show11() throws Exception {
        //3. 必须成功的删除
        cf.delete().guaranteed().forPath("/c");
}

/**
 * 回调
 * @throws Exception
 */
@Test
public void show12() throws Exception {
    //4. 回调
    cf.delete().guaranteed().inBackground(new BackgroundCallback(){
        @Override
        public void processResult(CuratorFramework curatorFramework, CuratorEvent curatorEvent) throws Exception {
            System.out.println("我被删除了~");
            System.out.println(curatorEvent);
        }
    }).forPath("/f");
    
    ==================================
          //4. 回调
        cf.delete().guaranteed().inBackground(new BackgroundCallback(){
            @Override
            public void processResult(CuratorFramework curatorFramework, CuratorEvent curatorEvent) throws Exception {
                System.out.println("我被删除了~");
                System.out.println(curatorEvent);
            }
        }).forPath("/f");

        while (true){

        }

}

监听

作用:可以获取节点的各种信息。

zk的监听机制(增删改事件):

1.nodecache 监听单个节点

.start (true) :如果设置为true,表示 开启缓存功能。在本地生成一个缓存区。保存在本地缓存区

​ 不设置true,默认开启。

/**
 * 监控节点
 * @throws Exception
 */
@Test
public void show13() throws Exception {
    //创建节点监听对象
    NodeCache nodeCache = new NodeCache(cf,"/a");

    //注册监听
    nodeCache.getListenable().addListener(new NodeCacheListener(){

        //指定监听后的事件
        @Override
        public void nodeChanged() throws Exception {
            System.out.println("节点变动了");
            byte[] data = nodeCache.getCurrentData().getData(); //获取节点中的信息
            System.out.println(data);
        }
    });

    //3. 开启
    nodeCache.start(true);

    while (true){

    }
}

2.pathchildrencache 监听特定节点的子节点 (不包含本身)

在构造器中指定是否起开缓存功能 【通过变量event可以获取该节点的状态信息】

测试:是否可以监听孙节点? 测试结果:不可以

//2. 绑定监听器
pathChildrenCache.getListenable().addListener(new PathChildrenCacheListener() {
    @Override
    public void childEvent(CuratorFramework client, PathChildrenCacheEvent event) throws Exception {
        System.out.println("子节点变化了~");
        System.out.println(event);
        //监听子节点的数据变更,并且拿到变更后的数据
        //1.获取类型
        PathChildrenCacheEvent.Type type = event.getType();
        //2.判断类型是否是update
        if(type.equals(PathChildrenCacheEvent.Type.CHILD_UPDATED)){
            System.out.println("数据变了!!!");
            byte[] data = event.getData().getData();
            System.out.println(new String(data));

        }
    }
});
//3. 开启
pathChildrenCache.start();

while (true){

}
}

3.treeCache 监听该节点的所有子孙节点

上述两种的结合。

场景发布/订阅功能:关注/提醒

【通过变量event可以获取该节点的状态信息】

public void testTreeCache() throws Exception {
    //1. 创建监听器
    TreeCache treeCache = new TreeCache(client,"/app2");

    //2. 注册监听
    treeCache.getListenable().addListener(new TreeCacheListener() {
        @Override
        public void childEvent(CuratorFramework client, TreeCacheEvent event) throws Exception {
            System.out.println("节点变化了");
            System.out.println(event);
        }
    });

    //3. 开启
    treeCache.start();

    while (true){

    }
}

分布式锁

定义:协调跨机器进程之间的同步问题

出现的原因: 由于分布式架构,导致在进行操作的时候已经不再是对同一个JVM进行操作,所以单纯的使用多线程锁机制在控制原子性已经不能满足需求。于是就出现了分布式锁组件 此处是使用的ZK的分布式锁组件,可以很好的解决跨机器进程之间的数据同步问题。

拥有分布式机制的技术:
1.基于缓存的分布式锁 redis、memcache【redis性能超好,缺点是如果redis集群 主服务器挂掉,可能从服务器都能获取到锁】
2.zookeeper分布式锁 curator (相对较高、但是性能稳定)
3.数据库层面分布式锁 悲观、乐观锁
(不会使用,因为数据库本身性能就低【修改的时候 对某表进行添加一条数据,第二个进程访问时查看,有拦截,无通行】)

案例 :卖票

package com.itheima.curator;

import org.apache.curator.RetryPolicy;
import org.apache.curator.framework.CuratorFramework;
import org.apache.curator.framework.CuratorFrameworkFactory;
import org.apache.curator.framework.recipes.locks.InterProcessMutex;
import org.apache.curator.retry.ExponentialBackoffRetry;

import java.util.concurrent.TimeUnit;

public class Ticket12306 implements Runnable{

    private int tickets = 10;//数据库的票数

    private InterProcessMutex lock ;  //定义分布式锁

    //连接zk
    public Ticket12306(){
        //重试策略
        RetryPolicy retryPolicy = new ExponentialBackoffRetry(3000, 10);
        //2.第二种方式
        //CuratorFrameworkFactory.builder();
        CuratorFramework client = CuratorFrameworkFactory.builder()
                .connectString("192.168.149.135:2181")
                .sessionTimeoutMs(60 * 1000)
                .connectionTimeoutMs(15 * 1000)
                .retryPolicy(retryPolicy)
                .build();

        //开启连接
        client.start();

        lock = new InterProcessMutex(client,"/lock");  //初始化锁
    }

    @Override
    //重写run方法开启线程
    public void run() {

        while(true){
            //获取锁
            try {
                //设置锁的每一次的等待时间和时间单位
                lock.acquire(3, TimeUnit.SECONDS);
                if(tickets > 0){

                    System.out.println(Thread.currentThread()+":"+tickets);
                    Thread.sleep(100);
                    tickets--;
                }
            } catch (Exception e) {
                e.printStackTrace();
            }finally {
                //释放锁
                try {
                    lock.release();
                } catch (Exception e) {
                    e.printStackTrace();
                } 
            }
        }
    }
}

分布式锁原理

(Curator已经对分布式锁进行了完全封装)

  • 定义:协调跨机器的进程之间的数据同步

  • 原理:

    ​ 客户端获取到锁的时候,会在对应的根节点下创建临时顺序节点

    • 临时:是为了防止偶发宕机情况下,lock对象不能释放,形成死锁。所以不是持久的 而是临时的
    • 顺序:是提供了对象的排队功能(按照顺序依次获得锁),为了找到最小节点!
假设此时有三个客户端访问lock节点。

1.每一个线程到达lock节点下,都会生成一个自己的临时顺序节点,并且获取当前节点下的所有临时节点。按照先后顺序依次获得该节点的锁。
2.如果发现自己没有拿到锁,就会对前一个临时顺序节点进行监听。监听其删除事件,一旦删除就再次进行判断自己是否是最小的节点,如果是获得锁,如果不是重新找到比自己小的临时节点注册监听事件

在这里插入图片描述

zk集群搭建

搭建集群要保证所有机器都是zk服务器

leader选举机制

在搭建集群的过程中,zk提供了自动选举机制。当集群中的主服务器出现问题不能正常使用的时候,那么启动选举机制。
选举机制是超过半数的服务器会作为新的主服务器使用。
规则:
	1. 依次按照自己启动的顺序来进行选举,当选举的票数达到半数就会使起当做新的leader 结束本次选举
	(简而言之就是 基数为中间(5台选第三台)   偶数为中间+1(四台选第三台))
	
#注意: 故障之后 只要zk服务器数量超过2  那么就会自动选举 如果小于  那么leader会修休眠直到其他zk服务器重现

在这里插入图片描述

角色职责

主机写  辅机同步读取 保证一致性

领导者: 只负责事务请求和数据同步的调度工作
跟随者: 处理查询请求  如果是事务请求则进行转发 将该请求转发给领导者
观察者: 作用与跟随者一致  只是不会进行投票选举

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值