Zookeeper

1.Zookeeper的介绍

介绍:

zookeeper原本是Hadoop中的一个组件,后独立出来,在apache上维护

zookeeper译名:动物园管理员

​ 管理着大数据中的各个组件

​ hdfs + mapreduce + hbase + hive

特点:

  • 分布式锁
  • 集群管理
  • 分布式节点管理
  • 统一管理配置信息

2.Zookeeper的架构

1.zookeeper的架构图

在这里插入图片描述

2.Znode的特点
znode分为四种节点

1.持久节点
	只要存储,会永远保存在zookeeper中
		
2.持久有序节点
	只要存储,会永久的保存在zookeeper中,并且会携带一个唯一并且有序的id,先进来的节点值相对较小
	
3.临时节点
	正常存储数据,当客户端断开连接后,自动删除节点信息
	
4.临时有序节点
	正常存储数据,当客户端断开连接后,自动删除节点信息,并且会携带一个唯一并且有序的id,先进来的节点值相对较小
3.监控机制

可以有多个客户端监听zookeeper的某一个znode
每当zookeeper被监听的节点值增删改后,会通知全部监听当前节点的客户端

3.Zookeeper的集群特点

1.zookeeper集群中的角色
1.leader
	leader管理着整个zookeeper集群中的全部节点,leader主要负责读写操作,和数据同步。
	当leader执行写操作后,会对外广播,当节点接收后,在本地写数据,并回调给leader-ack,当超过半数的从节点持久化完毕后,leader再次广播,可以commit了
	
2.follower
	追随leader的从节点,主要负责读操作
	当follower接收写操作后,会第一时间交给leader处理
	follower会发起leader选举的投票
	
3.observe
	追随leader的从节点,主要负责读操作
	当observe接收写操作后,会第一时间交给leader处理
	等待leader出现,直接追随,不会参与投票
	
4.looking
	looking节点会直接发起一次选举leader的投票
	
	如果leader不存在,让每一个非observe的节点参与投票,票数最高作为leader
		从looking变成follower追随leader的节点
	如果leader存在,直接变成follower追随当前leader节点
2.zookeeper存储数据的有序性(选举leader的关键性)
1.zookeeper在执行写操作时,是不会阻塞IO的。zookeeper为了保证有序性,会给每一个写操作的数据,编写一个全局唯一的zxid

2.zxid是一个64位的数字,前32位是由当前节点参与选举次数决定的,后32位是存储数据的全局唯一id,后存储的数据的zxid肯定大于先存储的

哪个节点的zxid越大,数据越新,会被选举为leader
在每一个zookeeper节点启动时,都会有一个全局唯一的myid(数字)
在选举leader时,如果zxid一致,根据myid的值,决定选举的leader是谁

4.搭建zookeeper集群

1.搭建三台zookeeper(在一台虚拟机中)

在虚拟机中解压三个文件分别为zk1,zk2,zk3

2.修改配置文件
1.进入zk1/conf/,复制一份zoo-siample.cfg,并改名为zoo.cfg
2.vi zoo.cfg,配置以下参数
	dataDir=../data
	dataLogDir=../log
	server.1=114.55.219.117:7001:8001
	server.2=114.55.219.117:7002:8002
	server.3=114.55.219.117:7003:8003
3.进入zk1/data目录下
	vi myid		创建该文件并在里面添加数字1
...
zk2和zk3和上述一致,只是myid中的值分别改为23,并且zoo.cfg中的client.port该为21822183
3.启动

启动zk1

​	cd zk1/bin/./zkServer.sh start

​	./zkServer.sh status

在这里插入图片描述
因为当前集群只有一个节点,再启动zk2

cd /usr/local/zk-cluster/zk2/bin/	#进入zk2的bin目录
./zkServer.sh start		#启动zk2
./zkServer.sh status	#查看zk2的服务状态

在这里插入图片描述
我们和上述一样启动zk3的服务并查看zk1的状态和zk3的状态
在这里插入图片描述在这里插入图片描述
这时停掉zk2的服务,再查看一下zk1和zk3的状态
在这里插入图片描述
我们可以发现当zk2的服务挂掉之后,zk3被选举为leader,因为在没有zxid的基础上,zk3的myid的值大于zk2的myid

此时,启动zookeeper客户端创建一个节点 如:
在这里插入图片描述
这时退出客户端并且将zk3的服务关闭
在这里插入图片描述
查看zk1的状态,发现这时zk1为leader了,因为zk1已经参与了节点的操作了,zxid大于zk2
注意:

 1. 启动集群时,如果先启动一台,那么集群是瘫痪的,无法使用

 2. 当集群中节点超过半数存货时,zk集群才可以正常运行

 3. 投票机制

    ​	测试效果:

    ​	1.启动zk1和zk2		->zk2为leader

    ​	2.启动zk3				  ->zk2为leader

    ​	3.关闭zk2				  ->zk3为leader

    ​	4.添加一条数据			-----------5.关闭zk3,启动zk2		->zk1为leader

5.Zookeeper的命令操作

1.查询节点
	ls  节点名称	->	查询当前节点下的字节点
	get   节点名称	->	查看当前节点的名称
	
2.创建节点
	create [-s] [-e] path data acl
	创建 [有序] [临时] 节点路径 节点数据 节点访问权限

3.修改节点
	set path data	->	修改节点的数据

4.删除节点
	delete path		->	删除没有字节的节点
	rmr path		->	直接递归删除节点(慎用)

5.查看节点状态
	stat path		->	查看节点的状态

6.通过Java的API操作

1.创建项目

  …

2.导入依赖
<!-- https://mvnrepository.com/artifact/com.101tec/zkclient -->
        <dependency>
            <groupId>com.101tec</groupId>
            <artifactId>zkclient</artifactId>
            <version>0.11</version>
        </dependency>

        <!-- https://mvnrepository.com/artifact/org.apache.curator/curator-recipes -->
        <dependency>
            <groupId>org.apache.curator</groupId>
            <artifactId>curator-recipes</artifactId>
            <version>4.1.0</version>
        </dependency>

3.连接zookeeper
public class TestConnect {

    @Test
    public void connect(){

        //重试策略,每隔三秒重试一次,一共重试两次
        RetryPolicy retryPolicy = new ExponentialBackoffRetry(3000,2);

        //连接zookeeper的客户端
        CuratorFramework cf = CuratorFrameworkFactory.builder()
                .connectString("114.55.219.117:2181,114.55.219.117:2182,114.55.219.117:2183")
                .connectionTimeoutMs(6000)
                .retryPolicy(retryPolicy)
                .build();

        //开启连接
        cf.start();
    }

}
4.CRUD
public class TestConnect {

    @Test
    public void connect(){

        //重试策略,每隔三秒重试一次,一共重试两次
        RetryPolicy retryPolicy = new ExponentialBackoffRetry(3000,2);

        //连接zookeeper的客户端
        CuratorFramework cf = CuratorFrameworkFactory.builder()
                .connectString("114.55.219.117:2181,114.55.219.117:2182,114.55.219.117:2183")
                .connectionTimeoutMs(6000)
                .retryPolicy(retryPolicy)
                .build();

        //开启连接
        cf.start();
        
        //======================================================
        
        //1.创建
        
        //创建节点
        String s = cf.create().forPath("/bb", "bilegebi".getBytes());
        System.out.println(s);
        
       
        //创建多级目录
        String s = cf.create().creatingParentsIfNeeded().forPath("/cc/dd", "hello world".getBytes());
        System.out.println(s);
        
        
        /**
         * 创建临时节点和有序数据
         * PERSISTENT:持久
         * PERSISTENT_SEQUENTIAL:持久有序
         * EPHEMERAL:临时
         * EPHEMERAL_SEQUENTIAL:临时有序
         */
        cf.create().creatingParentsIfNeeded().withMode(CreateMode.EPHEMERAL_SEQUENTIAL).forPath("/ab/ac","xxxx".getBytes());
        
        
        
        //2.删除
        
        //删除单个节点
        Void aVoid = cf.delete().forPath("/aa");
        //删除多级节点
        Void aVoid1 = cf.delete().deletingChildrenIfNeeded().forPath("/aa");
        System.out.println(aVoid);
        System.out.println(aVoid1);
        
        
        //3.修改
        
         //修改
        Stat stat = cf.setData().forPath("/aa", "哇哈哈哈哈哈".getBytes());
        System.out.println(stat);


		//4.查询
		
		//获取子节点
        List<String> list = cf.getChildren().forPath("/");
        list.forEach(System.out::println);

        //获取节点中的数据
        byte[] bytes = cf.getData().forPath("/aa");
        String s = new String(bytes);
        System.out.println(s);

        //检查节点是否存在,存在返回状态信息,不存在返回null
        Stat stat = cf.checkExists().forPath("/aa");
        System.out.println(stat);

        //======================================================
    }

}
5.监听
@Test
    public void listener() throws Exception {

        //重试策略,每隔三秒重试一次,一共重试两次
        RetryPolicy retryPolicy = new ExponentialBackoffRetry(3000,2);

        //连接zookeeper的客户端
        CuratorFramework cf = CuratorFrameworkFactory.builder()
                .connectString("114.55.219.117:2181,114.55.219.117:2182,114.55.219.117:2183")
                .connectionTimeoutMs(6000)
                .retryPolicy(retryPolicy)
                .build();

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

        //1.创建一个节点缓存
        NodeCache nodeCache = new NodeCache(cf,"/aa");
        nodeCache.start();
        //2.开始监听
        nodeCache.getListenable().addListener(new NodeCacheListener() {
            //当监听的节点发生变化调用该方法
            @Override
            public void nodeChanged() throws Exception {
                String path = nodeCache.getPath();
                System.out.println("变化的节点路径:"+path);

                Stat stat = nodeCache.getCurrentData().getStat();
                byte[] data = nodeCache.getCurrentData().getData();
                String path1 = nodeCache.getCurrentData().getPath();

                System.out.println("stat:"+stat);
                System.out.println("bytes"+new String(data));
                System.out.println("path1:"+path1);
            }
        });

        //关闭连接
//        cf.close();
        System.in.read();
    }

7.Zookeeper的分布式锁

1.创建一个config

@SpringBootConfiguration
public class CuratorFrameworkConfig {

    @Bean
    public CuratorFramework cf(){
        //重试策略,每隔三秒重试一次,一共重试两次
        RetryPolicy retryPolicy = new ExponentialBackoffRetry(3000,2);

        //连接zookeeper的客户端
        CuratorFramework cf = CuratorFrameworkFactory.builder()
                .connectString("114.55.219.117:2181,114.55.219.117:2182,114.55.219.117:2183")
                .connectionTimeoutMs(6000)
                .retryPolicy(retryPolicy)
                .build();

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

        //关闭连接
        cf.close();
        return cf;
    }
}

编写锁的测试类

@RestController
public class ZkLockController {

    private static int i = 10000;

    private static int j = 0;

    @Autowired
    private CuratorFramework cf;

    //公平锁
    @RequestMapping("/lock")
    public String lock(){
        InterProcessMutex lock = new InterProcessMutex(cf,"/lock");
        
        /**
         * zookeeper公平锁是基于它的临时有序节点实现的,当并发访问某一个接口后,
         * 会在zookeeper中创建一个临时有序节点,并且这些节点名称都是一样的,
         * 当时zookeeper会在名称后加上一个序号,每一个节点只监听比他序号小的,
         * 当节点发现他是最小 的就会获取锁资源,如果不是最小的就会一直在那等着。
         * @return
         */
        try {
            lock.acquire();
            System.out.println("获取锁对象");
            i--;
            j++;
            //释放锁
            lock.release();
            return "i:"+i+",j:"+j;
        } catch (Exception e) {
            System.out.println("获取锁失败");
            return "i:"+i+",j:"+j;
        }
    }

    /**
     *  非公平锁使用的是临时节点。公平锁使用的是临时有序节点
     *  用户想获取到锁资源,用户就尝试创建指定的一个节点,如果节点存在,说明锁资源正在占用,直接抛出异常,try-catch中给用户提示,如果创建节点成功,线程成功的获取到了锁资源.
     *  如果出现了死锁?
     *  因为创建的时临时节点,如果出现异常,那么客户端自动断开连接,临时节点自动被删除.
     * @return
     */
    @RequestMapping("/nlock")
    public String nlock(){
        //重试策略,每隔三秒重试一次,一共重试两次
        RetryPolicy retryPolicy = new ExponentialBackoffRetry(3000,2);

        //连接zookeeper的客户端
        CuratorFramework cf = CuratorFrameworkFactory.builder()
                .connectString("114.55.219.117:2181,114.55.219.117:2182,114.55.219.117:2183")
                .connectionTimeoutMs(6000)
                .retryPolicy(retryPolicy)
                .build();

        //开启连接
        cf.start();
        try {
            //加锁
            cf.create().withMode(CreateMode.EPHEMERAL).forPath("/nlock");
            i--;
            j++;
            //释放锁
            cf.delete().deletingChildrenIfNeeded().forPath("/nlock");
            return "i:"+i+",j:"+j;
        } catch (Exception e) {
            System.out.println("获取锁失败");
            return "cool cool";
        } finally {
            //关闭连接
            cf.close();
        }
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值